Skip to content

dimkr/tootik

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

          ..    .        ..                                        ..
 ...            .       ....                     ...
 ...   .     .   .   .   ... ..      .  .     .  . ...                 ...
 ..    .  .    ..   .         .    .. .       .. ..     .                   .
 .,     .               . . . ..             .  ..                  .        .
 .                .     . .. .. .... .       .  .  .               ..  ..
            .              ..   ...  .       .    .   .   .        ..  .   .
 .   .        .   ..        .    ..             ...' .. .          .   .    . .
 . .              . .    .  __     .     .__  _ __ ,; .'. .  .     ....
    . .          .         / /____  ___  /./_(_) /__  .'  ..         . .  . .
  ..      ... .    .  .   /.__/ _ \/ _ \/ __/./  '_/.   .       .. .   .    .
  .'   ...  .             \__/\___/\___/\__/_/_/\_\              .  .  . .
      .         .    .    . .    .    ...     ... .           .      ..
 ..  .. .   . .... ..  .  .         ..   .  .     .          .  .  ... ....' .
   ...  .      .   .  .. .  ... ...      . ..   ..         .,..    .....
 .   ..   ......             . .''.  .  ..          .         . .  ...
 ' .      .. ..  ..     . . ... ......::.   ..       .,.       .  .. ....    ..
 . ....  . .....     .  .. .  . ... . .,'.   .        ..          ,..  ..
 . .    .  .  . ..   .  .   .. .  .     ..     ..  .  . .       . . .        .'
   .  ....   '...                ...    . .  ..  .     ...     . '.   '     ...

# localhost.localdomain:8443

Welcome, fedinaut! localhost.localdomain:8443 is an instance of tootik, a federated nanoblogging service.

────

πŸ“» My radio
πŸ“ž Mentions
⚑️ Followed users
😈 My profile
πŸ“‘ This planet
πŸ”₯ Hashtags
πŸ”­ Find user
πŸ”Ž Search posts
πŸ“£ New post
βš™οΈ Settings
πŸ“Š Statistics
πŸ›Ÿ Help

Latest release Build status Go Reference

Overview

tootik is a federated nanoblogging service for the small internet. With tootik, you can interact with your friends, including those on Mastodon, Lemmy and other ActivityPub-compatible servers, from the comfort of a minimalistic, text-based interface in the small internet:

                         Gemini           ActivityPub (HTTPS)
                           β†’                     ⇄
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  Bob's Gemini client  β”œβ”€β”¬β”€β”€ tootik instance β”œβ”€β”¬β”€β”€ Another tootik instance β”‚
 β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β”‚2024-01-01 alice       β”‚ β”‚ β”‚$ ./tootik ...   β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚> Hi @bob and @carol!  β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”œβ”€β”€ Something else β”‚
 β”‚...                    β”‚ β”‚                     β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚                     β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         └── Mastodon instance β”œβ”€β”
               β”‚ Alice's Gemini client β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
               β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”
               β”‚2024-01-01 bob         β”‚              β”‚  Carol's web browser  β”‚
               β”‚> Hi @alice!           β”‚              β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
               β”‚...                    β”‚              │╔═╗ alice              β”‚
               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚β•šβ•β•             17h agoβ”‚
                                                      β”‚Hi @bob and @carol!    β”‚
                                                      β”‚                       β”‚
                                                      β”‚  ╔═╗ bob              β”‚
                                                      β”‚  β•šβ•β•           16h agoβ”‚
                                                      β”‚  Hi @alice!           β”‚
                                                      β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
                                                      β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”β”β”β”β”β”β”β”β”“β”‚
                                                      β”‚β”‚ Hola       │┃Publish┃│
                                                      β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”—β”β”β”β”β”β”β”β”›β”‚
                                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

tootik is lightweight, private and accessible social network:

  • Its UI is served over Gemini, Gopher, Finger and Guppy: there's a wide variety of clients to choose from and some work great on old devices.
  • Rich content is reduced to plain text and links: it's a fast, low-bandwidth UI suitable for screen readers.
  • Anonymity: you authenticate using a TLS client certificate and don't have to share your email address or real name.
  • No promoted content, tracking or analytics: social networking, with the slow and non-commercial vibe of the small internet.
  • It's a single static executable, making it easy to set up your own instance instead of joining an existing one.
  • All instance data is stored in a single file, a sqlite database that is easy to backup and restore.
  • It's lightweight: a <=$5/mo VPS or a SBC is more than enough for a small instance.
  • It implements the subset of ActivityPub required for its feature set but not more, to stay small, reliable and maintainable.
  • It's written in two languages (Go and SQL), making the codebase suitable for educational purposes and easy to hack on.
  • It's permissively-licensed.

Features

Using tootik

You can join an existing instance or set up your own.

Building

go generate ./migrations

Then:

go build ./cmd/tootik -tags fts5

or, to build a static executable:

go build -tags netgo,sqlite_omit_load_extension,fts5 -ldflags "-linkmode external -extldflags -static" ./cmd/tootik

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Most user-visible data is stored in 4 tables in tootik's database:

  1. notes, which contains Object objects that represent posts
  2. shares, which records "user A shared post B" relationships
  3. persons, which contains Actor objects that represent users
  4. follows, which records "user A follows user B" relationships

notes.author, shares.by, follows.follower and follows.followed point to a row in persons.

shares.note points to a row in notes.

β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” ┏━━━━━━━━━┓ ┏━━━━━━━━━┓
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ ┃ outbox  ┃ ┃  inbox  ┃
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ ┣━━━━━━━━━┫ ┣━━━━━━━━━┫
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ ┃activity ┃ ┃activity ┃
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ ┃sender   ┃ ┃sender   ┃
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ ┃...      ┃ ┃...      ┃
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ┗━━━━━━━━━┛ ┗━━━━━━━━━┛

Federation happens through two tables, inbox and outbox. Both contain Activity objects that represent actions performed by the users in persons.

inbox contains activities by users on other servers, while outbox contains activities of local users.

                    ┏━━━━━━━━━━━━━━━━━┓
                    ┃ gemini.Listener ┃
                    ┗━━━━━━━━┳━━━━━━━━┛
                    ┏━━━━━━━━┻━━━━━━━━┓
                    ┃  front.Handler  ┃
                    ┗━━━━━┳━━━━━━━━━━━┛
β”Œβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”Έβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”°β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  ┏━━━━━━━┻━━━━━━┓
                  ┃ fed.Resolver ┃
                  ┗━━━━━━━━━━━━━━┛

gemini.Listener is a Gemini server that handles requests through Handler. It adds rows to persons during new user registration and changes rows when users change properties like their display name.

Resolver is responsible for fetching Actors that represents users of other servers. The fetched objects are cached in persons.

                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚ gemini.Listener β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    ┏━━━━━━━━━━━β”₯  front.Handler   β”‚
    ┃           β””β”°β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”°β”˜
β”Œβ”€β”€β”€β”Έβ”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”Έβ”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”Έβ”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
                  β”‚ fed.Resolver β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

In addition, Gemini requests can:

  • Add rows to notes (new post)
  • Change rows in notes (post editing)
  • Add rows to shares (user shares a post)
  • Remove rows from shares (user no longer shares a post)
  • Add rows to follows (user A followed user B)
  • Remove rows from follows (user A unfollowed user B)
  • ...
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”‚ gemini.Listener β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   ┝━━━━━━━━━━━┓
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜           ┃
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”Έβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”°β”€β”˜ β””β”°β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”      ┏┻━━━━┻━━━━━┓
                  β”‚ fed.Resolver β”‚      ┃ fed.Queue ┃
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      ┗━━━━━━━━━━━┛

Each user action (post creation, post deletion, ...) is recorded as an Activity object written to outbox.

fed.Queue is responsible for sending activities to followers from other servers, if needed.

                                      ┏━━━━━━━━━━━━━━━┓
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   ┃ outbox.Mover  ┃
                β”‚ gemini.Listener β”‚   ┃ outbox.Poller ┃
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   ┃ fed.Syncer    ┃
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  ┗━━━┳━━━━━┳━━━━━┛
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   β”œβ”€β”€β”€β”€β”€β”€β•‚β”€β”€β”€β”€β”β”ƒ
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜      ┃    │┃
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”Έβ” β”Œβ”€β”΄β”Έβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜ β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”      β”Œβ”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
                  β”‚ fed.Resolver β”‚      β”‚ fed.Queue β”‚
                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

tootik may perform automatic actions in the name of the user:

  1. Follow the new account and unfollow the old one, if a followed user moved their account
  2. Update poll results for polls published by the user, and send the new results to followers
  3. Handle disagreement between follows rows for this user and what other servers know
                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚ outbox.Mover  β”‚
                β”‚ gemini.Listener β”‚   β”‚ outbox.Poller β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ fed.Syncer    β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ ┏━━━━━━━━━━━━━━┓
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”β”‚    ┏━━┫ fed.Listener ┣━━┓
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜      β”‚    β”‚β”‚    ┃  ┗━━━━━┳━━━━━━━━┛  ┃
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”΄β” β”Œβ”€β”΄β”΄β”€β”€β”€β”€β”Έβ”€β” β”Œβ”€β”€β”€β”€β”Έβ”€β”€β”€β”€β”      ┃
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚      ┃
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€      ┃
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚      ┃
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚      ┃
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚      ┃
β””β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”˜ β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      ┃
                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”      β”Œβ”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”                     ┃
                  β”‚ fed.Resolver β”‚      β”‚ fed.Queue β”‚                     ┃
                  β””β”€β”€β”€β”€β”€β”€β”€β”°β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                     ┃
                          ┃                                               ┃
                          ┃                                               ┃
                          ┃                                               ┃
                          ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Requests from other servers are handled by fed.Listener, a HTTP server.

It extracts the signature and key ID from a request using httpsig.Extract, uses Resolver to fetch the public key if needed, validates the request using Verify and inserts the received Activity object into inbox.

In addition, fed.Listener allows other servers to fetch public activity (like public posts) from outbox, so they can fetch some past activity by a newly-followed user.

                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚ outbox.Mover  β”‚
                β”‚ gemini.Listener β”‚   β”‚ outbox.Poller β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ fed.Syncer    β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”β”‚    β”Œβ”€β”€β”€ fed.Listener β”œβ”€β”€β”
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜      β”‚    β”‚β”‚    β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”΄β” β”Œβ”€β”΄β”΄β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”      β”‚
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€      β”‚
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚      β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚      β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚      β”‚
β””β”€β”€β”€β”°β”€β”€β”€β”˜ β””β”€β”€β”€β”°β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”°β”€β”€β”¬β”€β”˜ β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”°β”€β”€β”˜      β”‚
    ┃         ┃   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”    ┃ β”Œβ”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”     ┏━━━━━┻━━━━━━━┓ β”‚
    ┃         ┃   β”‚ fed.Resolver β”‚    ┃ β”‚ fed.Queue β”‚     ┃ inbox.Queue ┃ β”‚
    ┃         ┃   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    ┃ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     ┗━┳━┳━┳━━━━━━━┛ β”‚
    ┃         ┃           β”‚           ┗━━━━━━━━━━━━━━━━━━━━━┛ ┃ ┃         β”‚
    ┃         ┗━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ ┃         β”‚
    ┗━━━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛         β”‚
                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Once inserted into inbox, inbox.Queue processes the received activities:

  • Adds new posts received in Create activities to notes
  • Edits post in notes according to Update activities
  • Records Announce activities in shares
  • Marks a follower-followed relationship in follows as accepted, when the followed user sends an Accept activity
  • Adds a new row to follows when a remote user sends a Follow activity to a local user
  • ...
                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚ outbox.Mover  β”‚
                β”‚ gemini.Listener β”‚   β”‚ outbox.Poller β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ fed.Syncer    β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”β”‚    β”Œβ”€β”€β”€ fed.Listener β”œβ”€β”€β”
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜      β”‚    β”‚β”‚    β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”΄β” β”Œβ”€β”΄β”΄β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”      β”‚
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€      β”‚
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚      β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚      β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚      β”‚
β””β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”¬β”€β”˜ β””β”¬β”€β”€β”€β”€β”€β”€β”€β”°β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜      β”‚
    β”‚         β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”    β”‚ β”Œβ”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” ┃   β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” β”‚
    β”‚         β”‚   β”‚ fed.Resolver β”‚    β”‚ β”‚ fed.Queue β”‚ ┗━━━β”₯ inbox.Queue β”‚ β”‚
    β”‚         β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜    β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”¬β”€β”¬β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
    β”‚         β”‚           β”‚           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚         β”‚
    β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

When a remote user replies in a thread started by a local user, the received Activity is inserted into outbox and forwarded to all followers of the local user.

                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚ outbox.Mover  β”‚
                β”‚ gemini.Listener β”‚   β”‚ outbox.Poller β”‚
                β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚ fed.Syncer    β”‚
                β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  front.Handler   β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”β”‚    β”Œβ”€β”€β”€ fed.Listener β”œβ”€β”€β”
    β”‚           β””β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜      β”‚    β”‚β”‚    β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”Œβ”€β”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”΄β”€β”€β”€β”€β”€β”€β”€β”΄β” β”Œβ”€β”΄β”΄β”€β”€β”€β”€β”΄β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”      β”‚
β”‚ notes β”‚ β”‚ shares β”‚ β”‚ persons β”‚ β”‚ follows β”‚ β”‚ outbox  β”‚ β”‚  inbox  β”‚      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€      β”‚
β”‚object β”‚ β”‚note    β”‚ β”‚actor    β”‚ β”‚follower β”‚ β”‚activity β”‚ β”‚activity β”‚      β”‚
β”‚author β”‚ β”‚by      β”‚ β”‚...      β”‚ β”‚followed β”‚ β”‚sender   β”‚ β”‚sender   β”‚      β”‚
β”‚...    β”‚ β”‚...     β”‚ β”‚         β”‚ β”‚...      β”‚ β”‚...      β”‚ β”‚...      β”‚      β”‚
β””β”€β”€β”€β”¬β”€β”€β”€β”˜ β””β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”¬β”€β”˜ β””β”¬β”€β”€β”€β”€β”€β”€β”€β”¬β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜      β”‚
    β”‚         β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”    β”‚ β”Œβ”΄β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” β”‚   β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” β”‚
    β”‚         β”‚   β”‚ fed.Resolver β”‚    β”‚ β”‚ fed.Queue β”‚ └──── inbox.Queue β”‚ β”‚
    β”‚         β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”°β”€β”€β”€β”€β”˜    β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”¬β”€β”¬β”€β”¬β”€β”°β”€β”€β”€β”€β”€β”˜ β”‚
    β”‚         β”‚           β”‚ ┃         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ ┃       β”‚
    β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β•‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ ┃       β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β•‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ┃       β”‚
                          β””β”€β•‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‚β”€β”€β”€β”€β”€β”€β”€β”˜
                            ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

To display details like the user's name and speed up the verification of future incoming replies, inbox.Queue uses Resolver to fetch the Actor objects of mentioned users (if needed).

More Documentation

Credits and Legal Information

tootik is free and unencumbered software released under the terms of the Apache License Version 2.0; see LICENSE for the license text.

The ASCII art logo at the top was made using FIGlet.