A federated nanoblogging service with a Gemini frontend.
APACHE-2.0 License
.. . .. ..
... . .... ...
... . . . . ... .. . . . . ... ...
.. . . .. . . .. . .. .. . .
., . . . . .. . .. . .
. . . .. .. .... . . . . .. ..
. .. ... . . . . . .. . .
. . . .. . .. ...' .. . . . . .
. . . . . __ . .__ _ __ ,; .'. . . ....
. . . / /____ ___ /./_(_) /__ .' .. . . . .
.. ... . . . /.__/ _ \/ _ \/ __/./ '_/. . .. . . .
.' ... . \__/\___/\___/\__/_/_/\_\ . . . .
. . . . . . ... ... . . ..
.. .. . . .... .. . . .. . . . . . ... ....' .
... . . . .. . ... ... . .. .. .,.. .....
. .. ...... . .''. . .. . . . ...
' . .. .. .. . . ... ......::. .. .,. . .. .... ..
. .... . ..... . .. . . ... . .,'. . .. ,.. ..
. . . . . .. . . .. . . .. .. . . . . . . .'
. .... '... ... . . .. . ... . '. ' ...
# localhost.localdomain:8443
Welcome, fedinaut! localhost.localdomain:8443 is an instance of tootik, a federated nanoblogging service.
ββββ
π» My feed
π Mentions
β‘οΈ Followed users
π My profile
π‘ Local feed
ποΈ Communities
π₯ Hashtags
π View profile
π Search posts
π£ New post
βοΈ Settings
π Status
π Help
tootik is a text-based social network.
tootik is federated: users can join an existing server or set up their own instance. A tootik user can interact with others on the same instance, users on other tootik instances, Mastodon users, Lemmy users and users of other ActivityPub-compatible server.
Unlike other social networks, tootik doesn't have a browser-based interface or an app: instead, its minimalistic, text-based interface is served over Gemini:
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ββ
βββββββββββββββββββββββββ
βββββββββββββββββββββββββ
This makes tootik lightweight, private and accessible:
You can join an existing instance or set up your own.
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
βββββββββ ββββββββββ βββββββββββ βββββββββββ
β notes β β shares β β persons β β follows β
β£ββββββββ« β£βββββββββ« β£ββββββββββ« β£ββββββββββ«
βobject β βnote β βactor β βfollower β
βauthor β βby β β... β βfollowed β
β... β β... β β β β... β
βββββββββ ββββββββββ βββββββββββ βββββββββββ
Most user-visible data is stored in 4 tables in tootik's database:
notes
, which contains Object objects that represent postsshares
, which records "user A shared post B" relationshipspersons
, which contains Actor objects that represent usersfollows
, which records "user A follows user B" relationshipsnotes.author
, shares.by
, follows.follower
and follows.followed
point to rows 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.
ββββββββββββ βββββββββββββββββββ
β gmi.Wrap β£ββ« 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 using Handler. It adds rows to persons
during new user registration and changes rows when users change properties like their display name.
gemini.Listener provides Handler with a writer that builds a Gemini response and asynchronously sends it to the client in chunks, while Handler continues to handle the request and append more lines to the page.
Resolver is responsible for fetching Actors that represent users of other servers, using user@domain
pairs and WebFinger. The fetched objects are cached in persons
.
ββββββββββββ βββββββββββββββββββ
β gmi.Wrap βββ€ 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:
notes
(new post)notes
(post editing)shares
(user shares a post)shares
(user no longer shares a post)follows
(user A followed user B)follows
(user A unfollowed user B) ββββββββββββ βββββββββββββββββββ
β gmi.Wrap βββ€ gemini.Listener β
ββββββββββββ ββββββββββ¬βββββββββ
ββββββββββ΄ββββββββββ
βββββββββββββ€ front.Handler βββββββββββββ
β ββ¬βββββββββ¬ββββββββ¬β β
βββββ΄ββββ ββββββββ΄ββ ββββββ΄βββββ ββ΄βββββββββ βββΈβββββββ ββββββββββ
β notes β β shares β β persons β β follows β β outbox β β inbox β
βββββββββ€ ββββββββββ€ βββββββββββ€ βββββββββββ€ ββββββββββ€ ββββββββββ€
βobject β βnote β βactor β βfollower β βactivityβ βactivityβ
βauthor β βby β β... β βfollowed β βsender β βsender β
β... β β... β β β β... β β... β β... β
βββββββββ ββββββββββ ββββββ¬βββββ βββββββββ°ββ ββ°ββββββββ ββββββββββ
β ββ»βββββ»ββββββ
βββββββββ΄βββββββ β fed.Queue β
β fed.Resolver β βββββββββββββ
ββββββββββββββββ
User actions like post creation or deletion are recorded as Activity objects written to outbox
.
fed.Queue polls outbox
and delivers these activities to followers on other servers. It uses the deliveries
table to track delivery progress and retry failed deliveries.
βββββββββββββββββ
ββββββββββββ βββββββββββββββββββ β outbox.Mover β
β gmi.Wrap βββ€ 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.Queue β
β fed.Resolver β βββββββββββββ
ββββββββββββββββ
tootik may perform automatic actions in the name of the user:
follows
rows for this user and what other servers know βββββββββββββββββ
ββββββββββββ βββββββββββββββββββ β outbox.Mover β
β gmi.Wrap βββ€ 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.Queue β β
β fed.Resolver β βββββββββββββ β
βββββββββ°βββββββ β
β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
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 β
β gmi.Wrap βββ€ 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.Queue β β inbox.Queue β β
β β β fed.Resolver β β βββββββββββββ βββ³ββ³ββ³ββββββββ β
β β βββββββββ¬βββββββ βββββββββββββββββββββββ β β β
β βββββββββββββΏββββββββββββββββββββββββββββββββββββ β β
βββββββββββββββββββββββΏββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Once inserted into inbox
, inbox.Queue processes the received activities:
Create
activities to notes
notes
according to Update
activitiesAnnounce
activities in shares
follows
as accepted, when the followed user sends an Accept
activityfollows
when a remote user sends a Follow
activity to a local user βββββββββββββββββ
ββββββββββββ βββββββββββββββββββ β outbox.Mover β
β gmi.Wrap βββ€ 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.Queue βββββββ₯ inbox.Queue β β
β β β fed.Resolver β β βββββββββββββ βββ¬ββ¬ββ¬ββββββββ β
β β βββββββββ¬βββββββ βββββββββββββββββββββββ β β β
β βββββββββββββΌββββββββββββββββββββββββββββββββββββ β β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
Sometimes, a received or newly created local Activity is forwarded to the followers of a local user:
outbox
and forwarded to all followers of the local user.outbox
and forwarded to all community members. βββββββββββββββββ
ββββββββββββ βββββββββββββββββββ β outbox.Mover β
β gmi.Wrap βββ€ 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.Queue βββββββ€ inbox.Queue β β
β β β fed.Resolver β β βββββββββββββ βββ¬ββ¬ββ¬ββ°ββββββ β
β β βββββββββ¬ββ°βββββ βββββββββββββββββββββββ β β β β
β βββββββββββββΌββββββββββββββββββββββββββββββββββββ β β β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββ β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββ
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).
βββββββββββββββββ
ββββββββββββ βββββββββββββββββββ β outbox.Mover β
β gmi.Wrap βββ€ gemini.Listener β β outbox.Poller β
ββββββββββββ ββββββββββ¬βββββββββ β fed.Syncer β
ββββββββββ΄ββββββββββ βββββ¬ββββββ¬ββββββ ββββββββββββββββ
βββββββββββββ€ front.Handler ββββββββΌββββββ ββββ€ fed.Listener ββββββββ
β ββ¬βββββββββ¬ββββββββ¬β β ββ β βββββββ¬βββββββββ β
βββββ΄ββββ ββββββββ΄ββ ββββββ΄βββββ ββ΄ββββββββ΄β βββ΄β΄βββββ΄β βββββββ΄βββ ββββββββββ β
β notes β β shares β β persons β β follows β β outbox β β inbox β β feed β β
βββββββββ€ ββββββββββ€ βββββββββββ€ βββββββββββ€ ββββββββββ€ ββββββββββ€ β£βββββββββ« β
βobject β βnote β βactor β βfollower β βactivityβ βactivityβ βfollowerβ β
βauthor β βby β β... β βfollowed β βsender β βsender β βnote β β
β... β β... β β β β... β β... β β... β β... β β
βββ°ββ¬ββββ βββ°ββ¬βββββ ββββ°ββ¬βββββ ββββ°ββ¬βββ¬ββ ββ¬βββββββ¬β ββββββββ¬ββ ββββββββ³ββ β
β β β β βββββββββ β β β ββ΄βββββ΄βββββββ ββββββ΄βββββββββ β β
β β β β β βββββββββ΄βββββββ β β β fed.Queue βββββββ€ inbox.Queue β β β
β β β β β β fed.Resolver β β β βββββββββββββ βββ¬ββ¬ββ¬ββ¬ββββββ β β
β β β β β βββββββββ¬ββ¬βββββ β βββββββββββββββββββββββ β β β β β
β β β βββββββββββββΌββΌββββββββββββββββββββββββββββββββββ β β β β
β βββββββββββββββββββββββΌββΌββββββββββββββββββββββββββββββββββββ β β β
β β β βββΌββββββββββββββββββββββββββββββββββββββΌββββββββββββ
βββ»ββββββββββ»ββββ»ββββ βββββββββββββββββββββββββββββββββββββββ β
β inbox.FeedUpdater β£ββββββββββββββββ β
βββββββββββ³ββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
To speed up each user's feed, inbox.FeedUpdater periodically appends rows to the feed
table. This table holds all information that appears in the user's feed: posts written or shared by followed users, author information and more, eliminating the need for join
queries, slow filtering by post visibility, deduplication and sorting by time when a user views their feed. This table is indexed by user and time, allowing fast querying of a single feed page for a particular user.
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.