Tootik
A federated, text-based social network with a Gemini frontend
Install / Use
/learn @dimkr/TootikREADME
tootik
Overview
tootik is a text-based social network.
tootik is federated using ActivityPub: users can join an existing instance or set up their own, then interact with users on the same instance and users of compatible servers like Mastodon, Lemmy, Sharkey, Friendica, Akkoma, GoToSocial, Mitra and PieFed.
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:
- Its UI is Gemini-based: 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 and update it later.
- 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 from scratch (not forked from some other project) in two languages (Go and SQL), making the codebase suitable for educational purposes and easy to hack on.
- It's permissively-licensed.
Features
- Good compatibility with various fediverse servers
- Text posts, with 3 privacy levels
- Public
- To followers
- To mentioned users
- Sharing of public posts
- FEP-044f quote posts, without support for approval
- Users can follow each other to see non-public posts
- With support for manual approval of follow requests
- With support for Mastodon's follower synchronization mechanism, aka FEP-8fcf
- FEP-ef61 portable accounts
- Accounts on different servers use one Ed25519 keypair
- User activity is replicated across all servers
- Multi-choice polls
- Lemmy-style communities
- Follow to join
- Mention community in a public post to start thread
- Community sends posts and replies to all members
- Bookmarks
- Full-text search within posts
- Upload of posts and user avatars, over Titan
- Automatic deletion of old posts
- Account migration, in both directions
- Support for multiple client certificates
- Support for invite-only registration
Using tootik
You can join an existing instance or set up your own.
Interactive Demo
go generate ./migrations
go run -tags fts5 ./cmd/demo
Database Drivers
tootik supports two database/sql drivers:
github.com/mattn/go-sqlite3modernc.org/sqlite, enabled whenCGO_ENABLED=0
Building
go generate ./migrations
go build ./cmd/tootik -tags fts5
To build a static executable:
go generate ./migrations
CGO_ENABLED=0 go build ./cmd/tootik
To build a smaller executable without support for profiling, add the no_pprof build tag:
CGO_ENABLED=0 go build -tags no_pprof ./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:
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" relationships
notes.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 │
│... │ │... │ │ │ │... │ │... │ │... │
└───────┘ └────────┘ └─────────┘ └─────────┘ └────────┘ └────────┘
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.
┌──────────┐ ┌─────────────────┐
│ gmi.Wrap ├─┤ gemini.Listener │
└──────────┘ └────────┬────────┘
┌───────┴───────────┐
┏━━━━━━━━━━━┥ fron
