TASK-012: Initialize Chirp project — design spec, directory structure, README
- 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
This commit is contained in:
@@ -1,3 +1,70 @@
|
||||
# chirp-pwa
|
||||
# Chirp — Fediverse Microblogging PWA
|
||||
|
||||
Chirp — Fediverse microblogging PWA (chirp.myfacefeed.com)
|
||||
A custom Progressive Web App frontend for the Fediverse, powered by GoToSocial.
|
||||
|
||||
**Instance:** chirp.myfacefeed.com
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Browser / Mobile
|
||||
↓ HTTPS
|
||||
Chirp Go Web Server (this repo)
|
||||
↓ Mastodon-compatible API
|
||||
GoToSocial (ActivityPub backend)
|
||||
↓ PostgreSQL + MinIO
|
||||
```
|
||||
|
||||
## Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|-----------|
|
||||
| Backend | Go 1.22, GoToSocial v0.21.2 |
|
||||
| Auth | Zitadel (OIDC) |
|
||||
| Storage | PostgreSQL 16, Redis, MinIO |
|
||||
| Search | Meilisearch |
|
||||
| Frontend | Vanilla JS PWA (no framework) |
|
||||
| Deploy | Nginx + systemd on 38.242.152.38 |
|
||||
|
||||
## Project Layout
|
||||
|
||||
```
|
||||
cmd/server/ — main entry point
|
||||
internal/
|
||||
config/ — env-based config struct
|
||||
gts/ — GoToSocial API client
|
||||
proxy/ — API proxy (/api/v1/* passthrough)
|
||||
oauth/ — Zitadel OIDC integration
|
||||
db/ — chirp schema CRUD (bookmarks, lists, etc.)
|
||||
ws/ — WebSocket hub (GTS stream bridge)
|
||||
scheduler/ — post scheduler (Redis sorted set)
|
||||
search/ — Meilisearch client
|
||||
web/
|
||||
templates/ — Go HTML templates
|
||||
static/ — CSS, JS, icons
|
||||
sw/ — service worker
|
||||
deploy/
|
||||
k8s/ — Kubernetes manifests (future)
|
||||
nginx/ — nginx config
|
||||
tests/
|
||||
integration/ — integration tests
|
||||
e2e/ — end-to-end tests
|
||||
```
|
||||
|
||||
## Design Spec
|
||||
|
||||
Full technical specification: [chirp-design-spec.md](chirp-design-spec.md)
|
||||
|
||||
## Infrastructure Notes
|
||||
|
||||
- GoToSocial DB: PostgreSQL `gotosocial` (127.0.0.1:5432)
|
||||
- Chirp schema: PostgreSQL `chirp` (8 custom tables)
|
||||
- Redis: 127.0.0.1:6379 (sessions, queues, pub/sub)
|
||||
- Meilisearch: 127.0.0.1:7700 (search indexes)
|
||||
- MinIO: s3.myfacefeed.com / chirp-media bucket
|
||||
|
||||
## Status
|
||||
|
||||
Phase 1 (Infrastructure) — complete as of 2026-05-31
|
||||
Phase 2 (Go Server Core) — in progress
|
||||
Phase 3+ (PWA) — assigned to Claude3
|
||||
|
||||
@@ -0,0 +1,857 @@
|
||||
# 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>
|
||||
```
|
||||
Reference in New Issue
Block a user