Federated social media platform built with Go, ActivityPub, and Next.js. Run your own instance and communicate across the network.
Live Demo: https://splitter-red-phi.vercel.app
Backend API: https://splitter-m0kv.onrender.com/api/v1/health
- Overview
- Architecture
- Project Structure
- Quick Start
- API Reference
- Authentication
- Federation
- Configuration
- Testing
- Documentation Index
- Contributing
Splitter is a full-stack federated social network. Each instance is independent but communicates with others via the ActivityPub protocol β users on one instance can follow and exchange content with users on any other Splitter (or compatible) instance.
Core Features:
- Federated social graph β follow, posts, boosts, replies across instances
- End-to-end encrypted DMs β Ed25519 key-pairs per device; server never sees plaintext
- Dual authentication β password/JWT and DID (Decentralized Identity) cryptographic login
- Hashtag trending β regex extraction + real-time trending tab backed by PostgreSQL full-text search
- AI
@splitbot β mention@splitin any post to receive a synchronous AI reply (Gemini 1.5 Flash / GPT-4o-mini) - Automated bot population β GitHub Actions cron job seeds the network with realistic content every 30 minutes
- Circle visibility β posts visible only to curated "circle" members
- Admin & moderation β role management, content reports, AI-assisted screening
βββββββββββββββββββββββ HTTPS βββββββββββββββββββββββ
β Next.js Frontend β ββββββββββββββΊ β Go/Echo Backend β
β (Vercel) β β (Render) β
β React + Tailwind β β REST API + AP β
βββββββββββββββββββββββ ββββββββββββ¬βββββββββββ
β pgx/v5
βββββββββββββΌβββββββββββ
β PostgreSQL 15 β
β (Neon β serverless) β
βββββββββββββββββββββββββ
Federation:
Instance 1 βββββ ActivityPub (HTTP Signatures) βββββΊ Instance 2
splitter-m0kv.onrender.com splitter-2.onrender.com
| Component | Technology |
|---|---|
| Backend API | Go 1.24 / Echo v4 |
| Database | PostgreSQL 15 (Neon serverless) |
| ORM / Driver | jackc/pgx v5 |
| Frontend | Next.js 16 / React 19 / Tailwind CSS |
| Auth | bcrypt + JWT (RS256) / Ed25519 DID keypairs |
| Federation | ActivityPub (W3C), HTTP Signatures, WebFinger |
| AI | Google Gemini 1.5 Flash / OpenAI GPT-4o-mini |
| Bots | Python 3 + GitHub Actions cron |
| Hosting | Render (backend) + Vercel (frontend) + Neon (DB) |
splitter/
βββ cmd/
β βββ server/ # Main server entrypoint
β βββ migrate/ # Standalone migration runner
βββ internal/
β βββ auth/ # DID keypair generation & verification
β βββ config/ # Environment-based configuration
β βββ db/ # Database connection (Neon + SSL)
β βββ federation/ # ActivityPub delivery, signatures, peer health
β βββ handlers/ # HTTP request handlers (one file per domain)
β βββ helpers/ # Shared utility functions
β βββ middleware/ # JWT auth, optional auth, CORS
β βββ models/ # Domain model structs
β βββ repository/ # Data-access layer (pgx queries)
β βββ server/ # Router setup & middleware wiring
βββ migrations/ # Ordered SQL migration files
βββ scripts/
β βββ bots/ # Python bot population scripts
βββ tests/
β βββ docs/ # Testing documentation (01β08)
β βββ unit/ # Unit tests (auth, posts, replies, users, security)
β βββ integration/ # Integration test suite
β βββ e2e_test/ # End-to-end tests
β βββ load/ # Load tests (k6 / Go)
β βββ seeder/ # Test data seeder
β βββ results/ # Captured test output
βββ diagrams/ # Architecture, ER, sequence, class diagrams (Mermaid)
βββ .env.example # Environment variables template
βββ Dockerfile # Multi-stage Docker build
βββ docker-compose.instances.yml # Two-instance local federation setup
βββ Makefile # Common dev commands
βββ go.mod
| Tool | Version | Notes |
|---|---|---|
| Go | 1.21+ | go.dev/dl |
| Node.js | 18+ | nodejs.org |
| Docker | Any | For running psql migrations |
| Neon account | β | Free tier at neon.tech |
git clone <repository-url>
cd splitter
cp .env.example .envEdit .env with your Neon credentials:
DB_HOST=ep-your-endpoint.region.aws.neon.tech
DB_PORT=5432
DB_USER=neondb_owner
DB_PASSWORD=your-password
DB_NAME=neondb
PORT=8000
ENV=development
BASE_URL=http://localhost:8000
JWT_SECRET=change-this-to-a-secure-random-stringdocker run --rm postgres:15 psql \
'postgres://USER:PASS@HOST/DBNAME?sslmode=require' \
-f migrations/000_master_schema.sqlgo mod download
go run ./cmd/server
# β Server listening on http://localhost:8000Default admin account created on first startup:
Username: admin
Password: splitteradmin β οΈ Change immediately in production
cd ../Splitter-frontend
npm install
npm run dev
# β Frontend at http://localhost:3000docker compose -f docker-compose.instances.yml upThis spins up two backend instances that federate with each other locally.
Base URL: http://localhost:8000/api/v1
π = Requires Authorization: Bearer <jwt_token>
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Register with username/email/password |
POST |
/auth/login |
Login β returns JWT |
POST |
/auth/challenge |
Request a DID auth nonce |
POST |
/auth/verify |
Verify DID signature β returns JWT |
POST |
/auth/refresh π |
Rotate JWT |
POST |
/auth/logout π |
Invalidate token |
| Method | Endpoint | Description |
|---|---|---|
GET |
/users/:id |
Get user by ID |
GET |
/users/did |
Get user by DID |
GET π |
/users/me |
Get own profile |
PUT π |
/users/me |
Update profile |
DELETE π |
/users/me |
Delete account |
GET π |
/users/search |
Search users |
POST π |
/users/me/circle/:id |
Add user to circle |
DELETE π |
/users/me/circle/:id |
Remove from circle |
GET π |
/users/me/circle/:id/check |
Check circle membership |
| Method | Endpoint | Description |
|---|---|---|
GET |
/posts/public |
Public feed |
GET |
/posts/:id |
Get post |
GET |
/posts/user/:did |
Posts by user DID |
GET π |
/posts/feed |
Personalized feed (follows + own) |
POST π |
/posts |
Create post (multipart/form-data) |
PUT π |
/posts/:id |
Update post |
DELETE π |
/posts/:id |
Delete post |
| Method | Endpoint | Description |
|---|---|---|
GET |
/posts/:id/replies |
Threaded replies for a post |
POST π |
/posts/:id/replies |
Reply to a post |
| Method | Endpoint | Description |
|---|---|---|
POST π |
/users/:id/follow |
Follow user |
DELETE π |
/users/:id/follow |
Unfollow |
GET |
/users/:id/followers |
Followers list |
GET |
/users/:id/following |
Following list |
POST π |
/posts/:id/like |
Like post |
DELETE π |
/posts/:id/like |
Unlike post |
POST π |
/posts/:id/repost |
Boost/repost |
POST π |
/posts/:id/bookmark |
Bookmark post |
| Method | Endpoint | Description |
|---|---|---|
GET π |
/messages/threads |
All message threads |
GET π |
/messages/conversation/:userId |
Messages with a user |
POST π |
/messages/send |
Send encrypted message |
| Method | Endpoint | Description |
|---|---|---|
GET |
/federation/timeline |
Cross-instance federated timeline |
GET |
/federation/users |
Search users across instances |
POST |
/ap/users/:username/inbox |
ActivityPub inbox |
GET |
/ap/users/:username |
ActivityPub Actor JSON-LD |
GET |
/.well-known/webfinger |
WebFinger discovery |
| Method | Endpoint | Description |
|---|---|---|
GET |
/admin/users |
List all users |
PUT |
/admin/users/:id/role |
Change role |
POST |
/admin/users/:id/suspend |
Suspend account |
POST |
/admin/users/:id/unsuspend |
Unsuspend account |
GET |
/admin/moderation-requests |
Content reports queue |
POST |
/admin/moderation-requests/:id/approve |
Approve moderator |
Full annotated reference with request/response examples: API_ENDPOINTS.md
Standard username + password flow returning a JWT:
curl -X POST http://localhost:8000/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"alice","password":"secret"}'
# β {"token":"eyJ..."}Use the token as Authorization: Bearer <token> on subsequent requests.
Challenge-response using Ed25519 keypairs β the server never receives the private key:
1. POST /auth/challenge { did: "did:web:..." } β { challenge: "nonce" }
2. Client signs nonce with private key
3. POST /auth/verify { did, challenge, signature } β { token: "JWT" }
Private keys are stored locally (encrypted) in the browser. Recovery requires the encrypted backup file downloaded at registration.
Splitter implements a subset of ActivityPub:
Create Β· Follow Β· Accept Β· Like Β· Announce Β· Undo Β· Delete
All inbound federation POSTs require:
Signatureheader β HTTP Signatures (RSA-SHA256)Digestheader β SHA-256 body digestDateheader β within Β±30 seconds (keep clocks synced via NTP)
FEDERATION_ENABLED=true
FEDERATION_DOMAIN=your-instance-id
FEDERATION_URL=https://your-public-urlFor full well-known endpoints and ActivityPub routes, see API_ENDPOINTS.md.
All configuration is via environment variables. See .env.example for the full list.
| Variable | Required | Description |
|---|---|---|
DB_HOST |
β | PostgreSQL host (Neon endpoint) |
DB_PORT |
β | PostgreSQL port (default: 5432) |
DB_USER |
β | Database username |
DB_PASSWORD |
β | Database password |
DB_NAME |
β | Database name |
PORT |
β | HTTP server port |
ENV |
β | development or production |
JWT_SECRET |
β | Secret for JWT signing |
BASE_URL |
β | Public base URL of this instance |
SPLIT_BOT_API_KEY |
Optional | OpenAI / Gemini key for @split bot |
FEDERATION_ENABLED |
Optional | Enable ActivityPub federation |
FEDERATION_DOMAIN |
Optional | Canonical domain/ID for this instance |
FEDERATION_URL |
Optional | Public HTTPS URL for federation |
# Run all tests
make test
# Run with coverage report
make test-cover
# Test a specific package
go test ./tests/unit/auth -v
# Integration tests (requires live DB in .env)
go test ./tests/integration/... -v
# Load tests
go test ./tests/load -vTest documentation is in tests/docs/:
| Doc | Coverage |
|---|---|
01_UNIT_TESTING.md |
Unit test strategy & examples |
02_INTEGRATION_TESTING.md |
Integration flows |
03_E2E_TESTING.md |
End-to-end scenarios |
04_DATABASE_TESTING.md |
Schema & migration tests |
05_LOAD_TESTING.md |
k6 / Go load benchmarks |
06_TEST_SEEDING.md |
Data seeder usage |
07_TEST_REPORTS.md |
Report format & CI integration |
08_REGRESSION_TESTING.md |
Regression suite |
| Document | Description |
|---|---|
DEPLOYMENT.md |
Live services, Render + Neon setup, CI/CD, deploy checklist |
API_ENDPOINTS.md |
Full annotated API reference with cURL examples |
DATABASE_SCHEMA.md |
Schema tables, indexes, relationships |
CONTRIBUTING.md |
Dev workflow, code standards, extension recipes |
SECURITY.md |
Security model, DID auth, E2EE, threat model |
TROUBLESHOOTING.md |
Common issues β DB connection, federation, DID |
CODING_STANDARDS.md |
Go style guide & naming conventions |
OPS.md |
Operational runbooks |
DESIGN.md |
Design decisions & ADRs |
ROADMAP.md |
Planned features & milestones |
BOTS.md |
Bot system architecture |
GLOSSARY.md |
Domain terminology |
diagrams/ |
Architecture, ER, sequence, state diagrams (Mermaid) |
migrations/README.md |
Migration file inventory |
tests/docs/ |
Full testing documentation suite |
See CONTRIBUTING.md for:
- Development setup
- Branch & commit conventions
- Code standards (Go formatting, error handling, naming)
- How to add new endpoints, models, and handlers
- Developer recipes (ActivityPub extensions, theming, bots)
- PR checklist
MIT β see LICENSE.