865b20f0b4
- Add chirp-design-spec.md (857-line full technical specification) - Create Go server directory structure (cmd/, internal/*, deploy/, tests/) - Create PWA frontend directories (web/templates, web/static, web/sw) - Write README with architecture overview and stack table Infrastructure status as of 2026-05-31: - GoToSocial v0.21.2 running on :8085 - PostgreSQL 16, Redis, Meilisearch all configured - Nginx SSL termination for chirp.myfacefeed.com
858 lines
30 KiB
Markdown
858 lines
30 KiB
Markdown
# Chirp — Full Technical Design Specification
|
|
# chirp.myfacefeed.com
|
|
# Last updated: 2026-05-30
|
|
|
|
---
|
|
|
|
## 1. SYSTEM OVERVIEW — END TO END FLOWCHART
|
|
|
|
```
|
|
USER (browser / mobile)
|
|
│
|
|
│ HTTPS
|
|
▼
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ K3s Ingress (176.9.93.253) │
|
|
│ chirp.myfacefeed.com │
|
|
└─────────────────────┬───────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────┐
|
|
│ CHIRP GO WEB SERVER │
|
|
│ (K3s pod, mff namespace) │
|
|
│ │
|
|
│ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │
|
|
│ │ Landing Page │ │ PWA Shell │ │ Admin │ │
|
|
│ │ / │ │ /app/* │ │ /admin │ │
|
|
│ └──────────────┘ └──────────────┘ └──────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────┐ │
|
|
│ │ API PROXY LAYER │ │
|
|
│ │ /api/v1/* → GoToSocial pass-through │ │
|
|
│ │ /api/v2/* → Chirp custom extensions │ │
|
|
│ └──────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────┐ │
|
|
│ │ WEBSOCKET HUB (Go) │ │
|
|
│ │ Real-time: timeline, notifications, DMs │ │
|
|
│ │ Bridges GoToSocial streaming + custom events│ │
|
|
│ └──────────────────────────────────────────────┘ │
|
|
│ │
|
|
│ ┌──────────────────────────────────────────────┐ │
|
|
│ │ SCHEDULER (Go) │ │
|
|
│ │ Post scheduling, recurring jobs │ │
|
|
│ │ Backed by Redis job queue │ │
|
|
│ └──────────────────────────────────────────────┘ │
|
|
└──────────┬──────────────┬──────────────┬────────────┘
|
|
│ │ │
|
|
▼ ▼ ▼
|
|
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
|
│ GOTOSOCIAL │ │ MEILISEARCH │ │ REDIS │
|
|
│ (Go, K3s) │ │ (Rust, K3s) │ │ (K3s) │
|
|
│ │ │ │ │ │
|
|
│ ActivityPub │ │ Full-text │ │ Sessions │
|
|
│ Federation │ │ search idx │ │ Job queue │
|
|
│ REST API │ │ posts,users │ │ Rate limit │
|
|
│ WebSockets │ │ hashtags │ │ Cache │
|
|
│ Web Push │ └──────────────┘ └──────────────┘
|
|
│ OAuth2/OIDC │
|
|
└──────┬───────┘
|
|
│
|
|
├──────────────────────────┐
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────┐ ┌──────────────┐
|
|
│ POSTGRESQL │ │ MINIO │
|
|
│ (K3s) │ │ s3.mff.com │
|
|
│ │ │ │
|
|
│ GTS schema │ │ chirp-media │
|
|
│ chirp_* │ │ bucket │
|
|
│ schema │ │ (images, │
|
|
│ extensions │ │ video, │
|
|
└──────────────┘ │ audio) │
|
|
└──────────────┘
|
|
│
|
|
┌─────────────────────────┤
|
|
│ │
|
|
▼ ▼
|
|
┌──────────────┐ ┌──────────────┐
|
|
│ MEDIAMTX │ │ ZITADEL │
|
|
│ stream.mff │ │ auth.mff │
|
|
│ │ │ │
|
|
│ Live video │ │ SSO / OIDC │
|
|
│ RTMP/HLS │ │ Identity │
|
|
│ WebRTC │ │ provider │
|
|
└──────────────┘ └──────────────┘
|
|
│
|
|
▼
|
|
┌──────────────────────────────────────────┐
|
|
│ FEDIVERSE (Internet) │
|
|
│ Mastodon, Misskey, Pleroma, Pixelfed │
|
|
│ Any ActivityPub server worldwide │
|
|
└──────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 2. COMPONENTS — ROLES AND RESPONSIBILITIES
|
|
|
|
### GoToSocial (backend engine)
|
|
- ActivityPub server — handles all federation with the Fediverse
|
|
- Mastodon-compatible REST API (`/api/v1/`)
|
|
- WebSocket streaming (`wss://chirp.myfacefeed.com/api/v1/streaming`)
|
|
- Web Push notifications (v0.18+)
|
|
- OAuth2 + OIDC (integrates with Zitadel at auth.myfacefeed.com)
|
|
- Media storage via MinIO S3 (`chirp-media` bucket)
|
|
- PostgreSQL backend
|
|
|
|
### Chirp Go Web Server (custom build)
|
|
- Serves landing page + PWA shell
|
|
- API proxy: pass `/api/v1/*` through to GoToSocial
|
|
- Custom API extensions at `/api/v2/*` for missing features
|
|
- WebSocket hub bridging GoToSocial streaming + custom events
|
|
- Post scheduler (Redis-backed)
|
|
- Meilisearch integration for full-text search
|
|
- Service worker + web manifest (makes it a PWA)
|
|
- Push notification subscription management
|
|
|
|
### Meilisearch (search)
|
|
- Full-text search across posts, users, hashtags
|
|
- Indexes synced from GoToSocial via webhook + polling
|
|
- Go client: `github.com/meilisearch/meilisearch-go`
|
|
- RAM: practical ~200-400MB for small-medium instance
|
|
|
|
### Redis
|
|
- Session store for Chirp Go server
|
|
- Job queue for post scheduler
|
|
- Rate limiting
|
|
- Cache for expensive GoToSocial API responses
|
|
- Pub/sub for WebSocket hub fan-out
|
|
|
|
### PostgreSQL (shared instance, separate schemas)
|
|
- `gotosocial` schema — owned by GoToSocial, never touch directly
|
|
- `chirp` schema — owned by Chirp Go server, all custom extensions
|
|
- Same DB host, separate connection users
|
|
|
|
### MinIO (existing at s3.myfacefeed.com)
|
|
- New bucket: `chirp-media`
|
|
- GoToSocial configured to use it directly
|
|
- CDN-fronted via cdn.myfacefeed.com for serving media
|
|
|
|
---
|
|
|
|
## 3. DATABASE DESIGN
|
|
|
|
### GoToSocial (do not modify — reference only)
|
|
GoToSocial manages its own schema via internal migrations.
|
|
Key tables it owns (read-only for Chirp):
|
|
```
|
|
accounts — user profiles, actor info
|
|
statuses — posts
|
|
follows — follow relationships
|
|
notifications — notification records
|
|
media_attachments — uploaded media references
|
|
tokens — OAuth tokens
|
|
applications — registered OAuth apps
|
|
```
|
|
|
|
### Chirp Schema Extensions (chirp.*)
|
|
All custom tables live in the `chirp` PostgreSQL schema.
|
|
Chirp Go server connects as user `chirp_app` with access only to `chirp.*`.
|
|
|
|
```sql
|
|
-- ============================================================
|
|
-- BOOKMARKS
|
|
-- GoToSocial has /bookmark endpoint but no persistent store
|
|
-- We store the mapping here
|
|
-- ============================================================
|
|
CREATE TABLE chirp.bookmarks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
account_id TEXT NOT NULL, -- GoToSocial account ID
|
|
status_id TEXT NOT NULL, -- GoToSocial status ID
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(account_id, status_id)
|
|
);
|
|
|
|
-- ============================================================
|
|
-- LISTS
|
|
-- GoToSocial has list endpoints — we extend with metadata
|
|
-- ============================================================
|
|
CREATE TABLE chirp.lists (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
account_id TEXT NOT NULL,
|
|
title TEXT NOT NULL,
|
|
replies_policy TEXT DEFAULT 'list', -- list, followed, none
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE TABLE chirp.list_members (
|
|
list_id UUID REFERENCES chirp.lists(id) ON DELETE CASCADE,
|
|
account_id TEXT NOT NULL, -- member's GoToSocial account ID
|
|
added_at TIMESTAMPTZ DEFAULT NOW(),
|
|
PRIMARY KEY (list_id, account_id)
|
|
);
|
|
|
|
-- ============================================================
|
|
-- SCHEDULED POSTS
|
|
-- ============================================================
|
|
CREATE TABLE chirp.scheduled_posts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
account_id TEXT NOT NULL,
|
|
scheduled_at TIMESTAMPTZ NOT NULL,
|
|
status_text TEXT NOT NULL,
|
|
visibility TEXT DEFAULT 'public',
|
|
content_warning TEXT,
|
|
media_ids TEXT[], -- GoToSocial media attachment IDs
|
|
poll JSONB, -- poll options if applicable
|
|
in_reply_to_id TEXT,
|
|
language TEXT DEFAULT 'en',
|
|
sensitive BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
dispatched_at TIMESTAMPTZ, -- set when sent to GoToSocial
|
|
failed_at TIMESTAMPTZ,
|
|
error TEXT
|
|
);
|
|
|
|
-- ============================================================
|
|
-- CONVERSATION MUTES
|
|
-- ============================================================
|
|
CREATE TABLE chirp.conversation_mutes (
|
|
account_id TEXT NOT NULL,
|
|
status_id TEXT NOT NULL, -- root status of conversation
|
|
muted_at TIMESTAMPTZ DEFAULT NOW(),
|
|
PRIMARY KEY (account_id, status_id)
|
|
);
|
|
|
|
-- ============================================================
|
|
-- SEARCH INDEX SYNC TRACKING
|
|
-- Tracks what has been indexed in Meilisearch
|
|
-- ============================================================
|
|
CREATE TABLE chirp.search_sync (
|
|
status_id TEXT PRIMARY KEY,
|
|
indexed_at TIMESTAMPTZ,
|
|
updated_at TIMESTAMPTZ,
|
|
deleted_at TIMESTAMPTZ
|
|
);
|
|
|
|
-- ============================================================
|
|
-- PUSH NOTIFICATION SUBSCRIPTIONS
|
|
-- Web Push subscriptions per account/device
|
|
-- ============================================================
|
|
CREATE TABLE chirp.push_subscriptions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
account_id TEXT NOT NULL,
|
|
endpoint TEXT NOT NULL UNIQUE,
|
|
p256dh_key TEXT NOT NULL,
|
|
auth_key TEXT NOT NULL,
|
|
alert_follow BOOLEAN DEFAULT TRUE,
|
|
alert_mention BOOLEAN DEFAULT TRUE,
|
|
alert_reblog BOOLEAN DEFAULT TRUE,
|
|
alert_poll BOOLEAN DEFAULT TRUE,
|
|
alert_status BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- ============================================================
|
|
-- RATE LIMITING OVERRIDES (per account)
|
|
-- ============================================================
|
|
CREATE TABLE chirp.rate_limit_overrides (
|
|
account_id TEXT PRIMARY KEY,
|
|
requests_per_min INT DEFAULT 300,
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
-- indexes
|
|
CREATE INDEX ON chirp.bookmarks(account_id);
|
|
CREATE INDEX ON chirp.scheduled_posts(scheduled_at) WHERE dispatched_at IS NULL;
|
|
CREATE INDEX ON chirp.conversation_mutes(account_id);
|
|
CREATE INDEX ON chirp.push_subscriptions(account_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 4. MISSING GOTOSOCIAL FEATURES — HOW CHIRP HANDLES EACH
|
|
|
|
| Missing Feature | Where Handled | Approach |
|
|
|----------------|---------------|----------|
|
|
| Full-text search | Chirp Go + Meilisearch | Index all public posts + users. Custom `/api/v2/search` endpoint |
|
|
| Post scheduling | Chirp Go + Redis + chirp.scheduled_posts | Store in DB, Redis job fires at scheduled_at, POSTs to GoToSocial |
|
|
| Conversation muting | Chirp Go + chirp.conversation_mutes | Client-side filter on timeline; Go server filters WebSocket stream |
|
|
| Federated hashtag search | Chirp Go | Query multiple known instances via their public APIs, merge results |
|
|
| Shared/imported block lists | Chirp Go admin | Fetch known block lists (e.g. Oliphant, Seirdy), push to GoToSocial admin API |
|
|
| Video streaming | MediaMTX (already running) | stream.myfacefeed.com — RTMP ingest, HLS/WebRTC playback, link in post |
|
|
| Web signup flow | Chirp Go | Custom signup page POSTs to GoToSocial admin API to create account |
|
|
| Account import/export | Chirp Go | Export: query GoToSocial for follows/blocks, serialize CSV. Import: re-follow via API |
|
|
| Follow/block list import | Chirp Go | Parse Mastodon-format CSV, batch follow/block via GoToSocial API |
|
|
| Hashtag following | Chirp Go + chirp schema | Track in DB, filter WebSocket stream to inject matching posts |
|
|
| Trending (local+federated) | Chirp Go | Aggregate hashtag counts from GoToSocial + query remote instances |
|
|
|
|
---
|
|
|
|
## 5. FULL API SHEET
|
|
|
|
### 5A. GoToSocial Pass-Through (Chirp proxies these unchanged)
|
|
|
|
```
|
|
ACCOUNTS
|
|
GET /api/v1/accounts/verify_credentials
|
|
PATCH /api/v1/accounts/update_credentials
|
|
GET /api/v1/accounts/:id
|
|
GET /api/v1/accounts/:id/statuses
|
|
GET /api/v1/accounts/:id/followers
|
|
GET /api/v1/accounts/:id/following
|
|
POST /api/v1/accounts/:id/follow
|
|
POST /api/v1/accounts/:id/unfollow
|
|
POST /api/v1/accounts/:id/block
|
|
POST /api/v1/accounts/:id/unblock
|
|
POST /api/v1/accounts/:id/mute
|
|
POST /api/v1/accounts/:id/unmute
|
|
GET /api/v1/accounts/relationships
|
|
GET /api/v1/accounts/search
|
|
POST /api/v1/accounts/:id/note
|
|
|
|
STATUSES
|
|
POST /api/v1/statuses (create post)
|
|
GET /api/v1/statuses/:id
|
|
DELETE /api/v1/statuses/:id
|
|
PUT /api/v1/statuses/:id (edit post)
|
|
GET /api/v1/statuses/:id/context (thread view)
|
|
GET /api/v1/statuses/:id/reblogged_by
|
|
GET /api/v1/statuses/:id/favourited_by
|
|
POST /api/v1/statuses/:id/favourite
|
|
POST /api/v1/statuses/:id/unfavourite
|
|
POST /api/v1/statuses/:id/reblog
|
|
POST /api/v1/statuses/:id/unreblog
|
|
POST /api/v1/statuses/:id/bookmark
|
|
POST /api/v1/statuses/:id/unbookmark
|
|
POST /api/v1/statuses/:id/pin
|
|
POST /api/v1/statuses/:id/unpin
|
|
GET /api/v1/statuses/:id/history (edit history)
|
|
|
|
TIMELINES
|
|
GET /api/v1/timelines/home
|
|
GET /api/v1/timelines/public
|
|
GET /api/v1/timelines/tag/:hashtag
|
|
GET /api/v1/timelines/list/:id
|
|
|
|
NOTIFICATIONS
|
|
GET /api/v1/notifications
|
|
GET /api/v1/notifications/:id
|
|
POST /api/v1/notifications/clear
|
|
POST /api/v1/notifications/:id/dismiss
|
|
|
|
MEDIA
|
|
POST /api/v2/media (upload attachment)
|
|
GET /api/v1/media/:id
|
|
PUT /api/v1/media/:id (update description)
|
|
|
|
STREAMING (WebSocket)
|
|
GET /api/v1/streaming?stream=user
|
|
GET /api/v1/streaming?stream=public
|
|
GET /api/v1/streaming?stream=public:local
|
|
GET /api/v1/streaming?stream=hashtag&tag=name
|
|
GET /api/v1/streaming?stream=list&list=id
|
|
GET /api/v1/streaming?stream=direct
|
|
|
|
FILTERS
|
|
GET /api/v2/filters
|
|
POST /api/v2/filters
|
|
GET /api/v2/filters/:id
|
|
PUT /api/v2/filters/:id
|
|
DELETE /api/v2/filters/:id
|
|
|
|
LISTS
|
|
GET /api/v1/lists
|
|
POST /api/v1/lists
|
|
GET /api/v1/lists/:id
|
|
PUT /api/v1/lists/:id
|
|
DELETE /api/v1/lists/:id
|
|
GET /api/v1/lists/:id/accounts
|
|
POST /api/v1/lists/:id/accounts
|
|
DELETE /api/v1/lists/:id/accounts
|
|
|
|
POLLS
|
|
GET /api/v1/polls/:id
|
|
POST /api/v1/polls/:id/votes
|
|
|
|
INSTANCE
|
|
GET /api/v1/instance
|
|
GET /api/v2/instance
|
|
GET /api/v1/instance/peers
|
|
GET /api/v1/instance/rules
|
|
|
|
OAUTH
|
|
POST /api/v1/apps
|
|
GET /oauth/authorize
|
|
POST /oauth/token
|
|
POST /oauth/revoke
|
|
|
|
TRENDS
|
|
GET /api/v1/trends/tags
|
|
GET /api/v1/trends/statuses
|
|
GET /api/v1/trends/links
|
|
|
|
FOLLOW REQUESTS
|
|
GET /api/v1/follow_requests
|
|
POST /api/v1/follow_requests/:id/authorize
|
|
POST /api/v1/follow_requests/:id/reject
|
|
|
|
ADMIN (GoToSocial admin API)
|
|
GET /api/v1/admin/accounts
|
|
POST /api/v1/admin/accounts/:id/action
|
|
GET /api/v1/admin/reports
|
|
GET /api/v1/admin/domain_allows
|
|
POST /api/v1/admin/domain_allows
|
|
GET /api/v1/admin/domain_blocks
|
|
POST /api/v1/admin/domain_blocks
|
|
```
|
|
|
|
### 5B. Chirp Custom API Extensions (/api/v2/chirp/*)
|
|
|
|
```
|
|
SEARCH (Meilisearch-backed)
|
|
GET /api/v2/chirp/search
|
|
?q=query&type=accounts|statuses|hashtags
|
|
&federated=true|false
|
|
&from=username
|
|
&before=date&after=date
|
|
&limit=20&offset=0
|
|
|
|
SCHEDULED POSTS
|
|
GET /api/v2/chirp/scheduled_statuses
|
|
POST /api/v2/chirp/scheduled_statuses
|
|
body: { text, scheduled_at, visibility, cw, media_ids, poll }
|
|
GET /api/v2/chirp/scheduled_statuses/:id
|
|
PUT /api/v2/chirp/scheduled_statuses/:id
|
|
DELETE /api/v2/chirp/scheduled_statuses/:id
|
|
|
|
CONVERSATION MUTES
|
|
POST /api/v2/chirp/statuses/:id/mute_conversation
|
|
POST /api/v2/chirp/statuses/:id/unmute_conversation
|
|
GET /api/v2/chirp/muted_conversations
|
|
|
|
ACCOUNT IMPORT / EXPORT
|
|
GET /api/v2/chirp/export/follows → CSV download
|
|
GET /api/v2/chirp/export/blocks → CSV download
|
|
GET /api/v2/chirp/export/mutes → CSV download
|
|
POST /api/v2/chirp/import/follows → CSV upload, batch follow
|
|
POST /api/v2/chirp/import/blocks → CSV upload, batch block
|
|
|
|
BLOCK LIST SUBSCRIPTIONS (shared block lists)
|
|
GET /api/v2/chirp/admin/blocklist_subscriptions
|
|
POST /api/v2/chirp/admin/blocklist_subscriptions
|
|
body: { url, description }
|
|
DELETE /api/v2/chirp/admin/blocklist_subscriptions/:id
|
|
POST /api/v2/chirp/admin/blocklist_subscriptions/:id/sync
|
|
|
|
FEDERATED HASHTAG SEARCH
|
|
GET /api/v2/chirp/federated/tag/:hashtag
|
|
Queries known peer instances, merges + deduplicates results
|
|
|
|
TRENDING (enhanced)
|
|
GET /api/v2/chirp/trends/federated Merges trends from peer instances
|
|
|
|
WEB PUSH (Chirp-managed subscriptions)
|
|
POST /api/v2/chirp/push/subscription
|
|
body: { endpoint, keys: { p256dh, auth }, alerts: {...} }
|
|
PUT /api/v2/chirp/push/subscription
|
|
GET /api/v2/chirp/push/subscription
|
|
DELETE /api/v2/chirp/push/subscription
|
|
|
|
LIVE VIDEO (MediaMTX integration)
|
|
POST /api/v2/chirp/live/start Creates stream key, returns RTMP URL
|
|
DELETE /api/v2/chirp/live/:stream_key Ends stream
|
|
GET /api/v2/chirp/live/:stream_key HLS playback URL + viewer count
|
|
|
|
INSTANCE ADMIN (Chirp layer)
|
|
GET /api/v2/chirp/admin/stats
|
|
GET /api/v2/chirp/admin/search_index Meilisearch index stats
|
|
POST /api/v2/chirp/admin/search_index/rebuild
|
|
```
|
|
|
|
---
|
|
|
|
## 6. SEARCH — MEILISEARCH IMPLEMENTATION
|
|
|
|
### Index Design
|
|
```
|
|
Index: chirp_statuses
|
|
Searchable fields: content, account_username, account_display_name, tags
|
|
Filterable: account_id, visibility, language, created_at, tags
|
|
Sortable: created_at, favourites_count, reblogs_count
|
|
|
|
Index: chirp_accounts
|
|
Searchable fields: username, display_name, note (bio), fields
|
|
Filterable: instance, discoverable
|
|
Sortable: followers_count
|
|
|
|
Index: chirp_tags
|
|
Searchable fields: name
|
|
Sortable: uses_week, uses_total
|
|
```
|
|
|
|
### Sync Strategy
|
|
```
|
|
1. Initial index: bulk fetch from GoToSocial admin API, batch insert
|
|
2. Real-time sync: subscribe to GoToSocial WebSocket stream
|
|
- on Create → index new status
|
|
- on Delete → remove from index
|
|
- on Update → re-index status
|
|
3. Chirp Go indexer runs as goroutine, channels for queue
|
|
4. Meilisearch tasks are async — track task ID in chirp.search_sync
|
|
5. Re-index job runs nightly to catch any gaps
|
|
```
|
|
|
|
### Go Client
|
|
```go
|
|
import "github.com/meilisearch/meilisearch-go"
|
|
|
|
client := meilisearch.New("http://meilisearch:7700",
|
|
meilisearch.WithAPIKey("MEILI_MASTER_KEY"))
|
|
|
|
index := client.Index("chirp_statuses")
|
|
index.AddDocuments(docs, "id")
|
|
index.Search("golang", &meilisearch.SearchRequest{
|
|
Limit: 20,
|
|
Filter: "visibility = public",
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## 7. STREAMING — WEBSOCKET ARCHITECTURE
|
|
|
|
GoToSocial provides native WebSocket streaming at `/api/v1/streaming`.
|
|
Chirp Go server bridges it and adds custom event types.
|
|
|
|
```
|
|
User Browser (WebSocket)
|
|
│
|
|
▼
|
|
Chirp Go WebSocket Hub
|
|
│
|
|
┌────┴─────────────────────────────┐
|
|
│ │
|
|
▼ ▼
|
|
GoToSocial Streaming Chirp Custom Events
|
|
/api/v1/streaming - scheduled_post_sent
|
|
(user, public, hashtag, list) - conversation_muted
|
|
- search_result_push
|
|
- live_stream_started
|
|
```
|
|
|
|
### Hub Design (Go)
|
|
```
|
|
Hub struct {
|
|
clients map[*Client]bool
|
|
broadcast chan Event
|
|
register chan *Client
|
|
unregister chan *Client
|
|
rooms map[string]map[*Client]bool // room = stream type
|
|
}
|
|
```
|
|
- One goroutine per GoToSocial WebSocket connection
|
|
- Redis pub/sub for fan-out across multiple Chirp pod replicas
|
|
- Events: `update`, `notification`, `delete`, `filters_changed` + Chirp custom types
|
|
|
|
---
|
|
|
|
## 8. POST SCHEDULER — REDIS JOB QUEUE
|
|
|
|
```
|
|
POST /api/v2/chirp/scheduled_statuses
|
|
│
|
|
▼
|
|
1. Store in chirp.scheduled_posts (PostgreSQL)
|
|
2. Push job to Redis sorted set (score = unix timestamp of scheduled_at)
|
|
│
|
|
▼
|
|
Chirp Scheduler Goroutine (runs every 30 seconds)
|
|
│
|
|
ZRANGEBYSCORE chirp:scheduled 0 now()
|
|
│
|
|
▼
|
|
For each due job:
|
|
1. POST to GoToSocial /api/v1/statuses with stored content
|
|
2. Mark chirp.scheduled_posts.dispatched_at = now()
|
|
3. Remove from Redis sorted set
|
|
4. On failure: set failed_at, store error, retry up to 3x
|
|
```
|
|
|
|
---
|
|
|
|
## 9. ACTIVITYPUB — WHAT GOTOSOCIAL HANDLES
|
|
|
|
GoToSocial manages all ActivityPub automatically. No custom AP code needed in Chirp.
|
|
|
|
| Activity | Who sends | Who receives |
|
|
|----------|-----------|-------------|
|
|
| Create (Note) | GoToSocial | Remote servers |
|
|
| Delete | GoToSocial | Remote servers |
|
|
| Announce (boost) | GoToSocial | Remote servers |
|
|
| Like | GoToSocial | Remote servers |
|
|
| Follow/Unfollow | GoToSocial | Remote servers |
|
|
| Accept/Reject | GoToSocial | Remote servers |
|
|
| Update (profile/post) | GoToSocial | Remote servers |
|
|
| Move (migration) | GoToSocial | Remote servers |
|
|
| Flag (report) | GoToSocial | Remote servers |
|
|
|
|
Federation endpoints GoToSocial exposes (do not intercept):
|
|
```
|
|
GET /.well-known/webfinger
|
|
GET /.well-known/nodeinfo
|
|
GET /nodeinfo/2.0
|
|
GET /users/:username (Actor object)
|
|
GET /users/:username/inbox (AP inbox)
|
|
POST /users/:username/inbox (receive AP activities)
|
|
GET /users/:username/outbox
|
|
GET /users/:username/followers
|
|
GET /users/:username/following
|
|
```
|
|
|
|
---
|
|
|
|
## 10. OAUTH / AUTHENTICATION FLOW
|
|
|
|
GoToSocial supports OIDC — connect to Zitadel (already at auth.myfacefeed.com):
|
|
|
|
```
|
|
User clicks Login
|
|
│
|
|
▼
|
|
Chirp Go → redirect to Zitadel (auth.myfacefeed.com)
|
|
│
|
|
▼ (user authenticates with Zitadel)
|
|
│
|
|
Zitadel → callback to GoToSocial OIDC callback
|
|
│
|
|
GoToSocial issues OAuth token
|
|
│
|
|
Chirp Go stores token in Redis session
|
|
│
|
|
User is logged in — token used for all API calls
|
|
```
|
|
|
|
Benefits:
|
|
- SSO across entire MFF platform (same Zitadel login as Nextcloud, Mattermost, etc.)
|
|
- No separate password for Chirp
|
|
- Chirp Go never sees raw passwords
|
|
|
|
---
|
|
|
|
## 11. PWA SPECIFICS
|
|
|
|
### Web Manifest (/manifest.json)
|
|
```json
|
|
{
|
|
"name": "Chirp",
|
|
"short_name": "Chirp",
|
|
"start_url": "/app",
|
|
"display": "standalone",
|
|
"background_color": "#0f172a",
|
|
"theme_color": "#2563eb",
|
|
"icons": [
|
|
{ "src": "/icons/192.png", "sizes": "192x192", "type": "image/png" },
|
|
{ "src": "/icons/512.png", "sizes": "512x512", "type": "image/png" },
|
|
{ "src": "/icons/512-maskable.png", "sizes": "512x512",
|
|
"type": "image/png", "purpose": "maskable" }
|
|
],
|
|
"shortcuts": [
|
|
{ "name": "Home", "url": "/app/home" },
|
|
{ "name": "Notifications", "url": "/app/notifications" },
|
|
{ "name": "Compose", "url": "/app/compose" }
|
|
]
|
|
}
|
|
```
|
|
|
|
### Service Worker Capabilities
|
|
- Cache shell (app HTML/CSS/JS) for offline load
|
|
- Cache recent timeline posts for offline reading
|
|
- Background sync: queue posts written offline, send when back online
|
|
- Push notifications: receive Web Push, display even when app closed
|
|
- Share target: receive shared URLs from other apps
|
|
|
|
---
|
|
|
|
## 12. GO CODE STRUCTURE
|
|
|
|
```
|
|
chirp/
|
|
├── cmd/
|
|
│ └── chirp/
|
|
│ └── main.go entry point
|
|
│
|
|
├── internal/
|
|
│ ├── api/
|
|
│ │ ├── proxy.go GoToSocial API pass-through
|
|
│ │ ├── search.go /api/v2/chirp/search handlers
|
|
│ │ ├── scheduler.go scheduled posts handlers
|
|
│ │ ├── mutes.go conversation mute handlers
|
|
│ │ ├── push.go Web Push handlers
|
|
│ │ ├── live.go MediaMTX live video handlers
|
|
│ │ ├── import_export.go CSV import/export
|
|
│ │ └── admin.go admin endpoints
|
|
│ │
|
|
│ ├── ws/
|
|
│ │ ├── hub.go WebSocket hub
|
|
│ │ ├── client.go per-connection client
|
|
│ │ └── bridge.go GoToSocial stream bridge
|
|
│ │
|
|
│ ├── search/
|
|
│ │ ├── meilisearch.go search client wrapper
|
|
│ │ ├── indexer.go real-time indexing goroutine
|
|
│ │ └── sync.go nightly re-index job
|
|
│ │
|
|
│ ├── scheduler/
|
|
│ │ ├── scheduler.go Redis job runner
|
|
│ │ └── dispatcher.go posts to GoToSocial
|
|
│ │
|
|
│ ├── db/
|
|
│ │ ├── db.go PostgreSQL connection pool
|
|
│ │ ├── bookmarks.go
|
|
│ │ ├── lists.go
|
|
│ │ ├── scheduled.go
|
|
│ │ ├── mutes.go
|
|
│ │ └── push.go
|
|
│ │
|
|
│ ├── gts/
|
|
│ │ └── client.go GoToSocial API client (typed)
|
|
│ │
|
|
│ ├── push/
|
|
│ │ └── webpush.go Web Push sender (VAPID)
|
|
│ │
|
|
│ └── config/
|
|
│ └── config.go env-based config
|
|
│
|
|
├── web/
|
|
│ ├── static/
|
|
│ │ ├── sw.js service worker
|
|
│ │ ├── manifest.json
|
|
│ │ ├── app.js PWA app bundle
|
|
│ │ └── app.css
|
|
│ └── templates/
|
|
│ ├── landing.html public landing page
|
|
│ └── app.html PWA shell (single page)
|
|
│
|
|
├── migrations/
|
|
│ └── *.sql chirp schema migrations
|
|
│
|
|
├── Dockerfile (minimal — scratch or alpine)
|
|
├── go.mod
|
|
└── go.sum
|
|
```
|
|
|
|
---
|
|
|
|
## 13. K3S DEPLOYMENT
|
|
|
|
```yaml
|
|
# 3 pods: gotosocial, chirp, meilisearch
|
|
# Shared: postgresql (existing), redis (existing), minio (existing)
|
|
|
|
Deployments:
|
|
gotosocial 1 replica ~350MB RAM chirp-media S3 bucket
|
|
chirp 2 replicas ~100MB RAM (Go is lean)
|
|
meilisearch 1 replica ~300MB RAM PVC for index data
|
|
|
|
Services:
|
|
gotosocial ClusterIP :8080
|
|
chirp ClusterIP :3000
|
|
meilisearch ClusterIP :7700 (internal only — never exposed)
|
|
|
|
Ingress:
|
|
chirp.myfacefeed.com → chirp :3000
|
|
|
|
Total new RAM: ~750MB-1GB
|
|
(vs Mastodon which would need 2-4GB)
|
|
```
|
|
|
|
---
|
|
|
|
## 14. BUILD ORDER (Implementation Sequence)
|
|
|
|
```
|
|
Phase 1 — Backend up
|
|
1. GoToSocial K3s deployment + MinIO bucket + PostgreSQL DB
|
|
2. Zitadel OIDC config for GoToSocial
|
|
3. chirp schema migrations
|
|
4. Meilisearch deployment + initial index
|
|
|
|
Phase 2 — Chirp Go server core
|
|
5. API proxy (pass-through to GoToSocial)
|
|
6. WebSocket hub + GoToSocial stream bridge
|
|
7. Session management (Redis)
|
|
8. OAuth flow with Zitadel
|
|
|
|
Phase 3 — PWA frontend
|
|
9. Landing page (chirp.myfacefeed.com public view)
|
|
10. App shell + service worker + manifest
|
|
11. Timeline view (home, public, local)
|
|
12. Compose post, reply, boost, favourite
|
|
13. Profile pages
|
|
14. Notifications
|
|
|
|
Phase 4 — Custom features (missing from GoToSocial)
|
|
15. Search (Meilisearch integration)
|
|
16. Post scheduling (Redis scheduler)
|
|
17. Conversation muting
|
|
18. Import/export follows/blocks
|
|
19. Shared block lists
|
|
20. Live video integration (MediaMTX)
|
|
|
|
Phase 5 — Polish
|
|
21. Push notifications (Web Push + VAPID)
|
|
22. Offline support (service worker caching)
|
|
23. PWA install prompt
|
|
24. Mobile responsive polish
|
|
25. Admin panel
|
|
```
|
|
|
|
---
|
|
|
|
## 15. ENVIRONMENT CONFIG (Chirp Go Server)
|
|
|
|
```bash
|
|
# GoToSocial
|
|
GTS_HOST=gotosocial:8080
|
|
GTS_ADMIN_TOKEN=<admin token>
|
|
|
|
# PostgreSQL (chirp schema)
|
|
CHIRP_DB_HOST=postgresql:5432
|
|
CHIRP_DB_NAME=chirp
|
|
CHIRP_DB_USER=chirp_app
|
|
CHIRP_DB_PASS=<password>
|
|
|
|
# Redis
|
|
REDIS_HOST=redis:6379
|
|
REDIS_PASS=<password>
|
|
|
|
# Meilisearch
|
|
MEILI_HOST=http://meilisearch:7700
|
|
MEILI_KEY=<master key>
|
|
|
|
# OAuth / Zitadel
|
|
OIDC_ISSUER=https://auth.myfacefeed.com
|
|
OIDC_CLIENT_ID=chirp
|
|
OIDC_CLIENT_SECRET=<secret>
|
|
|
|
# Web Push (VAPID keys — generate once)
|
|
VAPID_PUBLIC_KEY=<key>
|
|
VAPID_PRIVATE_KEY=<key>
|
|
VAPID_SUBJECT=mailto:admin@myfacefeed.com
|
|
|
|
# MinIO
|
|
S3_ENDPOINT=s3.myfacefeed.com
|
|
S3_BUCKET=chirp-media
|
|
S3_ACCESS_KEY=<key>
|
|
S3_SECRET_KEY=<key>
|
|
|
|
# MediaMTX
|
|
MEDIAMTX_API=http://mediamtx:9997
|
|
MEDIAMTX_RTMP_HOST=rtmp://stream.myfacefeed.com
|
|
|
|
# App
|
|
CHIRP_HOST=https://chirp.myfacefeed.com
|
|
CHIRP_PORT=3000
|
|
SESSION_SECRET=<random 64 bytes>
|
|
```
|