chore: Dockerfile, README, LICENSE, .env.example, CI workflows (lint/test/deploy)
Some checks failed
lint / lint (push) Has been cancelled
test / test (push) Has been cancelled
lint / lint (pull_request) Failing after 5m3s
test / test (pull_request) Failing after 18m48s

This commit is contained in:
2026-05-14 15:15:18 +02:00
parent ed744046f4
commit 057f10cb69
7 changed files with 328 additions and 33 deletions

119
README.md Normal file
View File

@@ -0,0 +1,119 @@
# Thoughts
A self-hosted microblogging server with full ActivityPub federation. Write short posts, follow people on Mastodon and other Fediverse servers, and receive their posts in your feed. Built in Rust with a Next.js frontend.
## Features
- Short-form posts (thoughts) with replies, boosts, and likes
- Full ActivityPub federation — follow/unfollow remote actors, accept/reject followers, federated content broadcast as `Note` objects, paginated outbox, NodeInfo discovery, WebFinger, shared inbox, actor profile sync
- Federation moderation — per-instance domain blocking, per-user actor blocking with `Block` activity delivery, delivery filter excludes blocked actors and blocked-domain inboxes
- Async event fan-out via NATS — notifications and AP delivery run in a separate worker process
- JWT authentication (Bearer token)
- OpenAPI documentation at `/docs` (Swagger UI) and `/scalar` (Scalar)
- Full-text search over thoughts and users via PostgreSQL trigram indexes
- Top friends — pin up to 5 users as highlighted contacts
- API keys for third-party client access
- Home feed, public feed, and per-user thought timelines
## Architecture
Hexagonal (Ports & Adapters) with Domain-Driven Design:
```
domain — pure types and port trait definitions, no external deps
application — use cases and event processing services (business logic)
api-types — shared REST API request/response DTOs
presentation — Axum HTTP router, OpenAPI spec, composition root for the API process
bootstrap — binary: thoughts (API server)
worker — binary: thoughts-worker (event consumer — notifications, AP fan-out)
adapters/
auth — JWT issuance and validation, Argon2 password hashing
postgres — PostgreSQL repositories for all domain entities
postgres-search — PostgreSQL trigram full-text search
postgres-federation — PostgreSQL-backed federation repository
activitypub-base — core ActivityPub protocol types, ActivityPubService, federation middleware
activitypub — project-specific AP wiring (ThoughtsObjectHandler, inbox/outbox)
nats — NATS transport implementing Transport + MessageSource ports
event-payload — shared event serialization DTOs
event-transport — Transport trait + EventPublisherAdapter / MessageSource + EventConsumerAdapter
```
## Prerequisites
- Rust stable (1.80+)
- PostgreSQL 15+
- NATS (optional — federation and notifications still work without it, events queue in-process)
## Environment Variables
Copy `.env.example` to `.env` and fill in your values:
```env
DATABASE_URL=postgres://postgres:password@localhost:5432/thoughts
JWT_SECRET=change-me
BASE_URL=http://localhost:3000
NATS_URL=nats://localhost:4222 # optional
```
See `.env.example` for all available options.
## Run
```bash
# API server (runs migrations automatically on startup)
cargo run -p bootstrap
# Event worker — federation fan-out and notifications (separate terminal)
cargo run -p worker
```
Both processes share the same PostgreSQL database. The worker is optional but required for ActivityPub delivery to remote servers.
## Test
```bash
# Unit tests — no database required
cargo test -p application
# Full workspace (requires DATABASE_URL pointing to a running PostgreSQL)
cargo test --workspace
```
The `application` crate contains unit tests for all event services and use cases backed by in-memory fakes from `domain`'s `test-helpers` feature. These are the fastest feedback loop for business logic.
## API
All REST endpoints are under the root path. Authentication uses `Authorization: Bearer <token>` obtained from `POST /auth/login`.
Interactive API documentation is available at runtime:
- **Swagger UI** — `http://localhost:3000/docs`
- **Scalar** — `http://localhost:3000/scalar`
## Docker
The image contains both `thoughts` (API server) and `thoughts-worker` (event processor). Run them as separate containers:
```bash
docker build -t thoughts .
# API server
docker run -p 3000:3000 \
-e DATABASE_URL=postgres://postgres:password@db:5432/thoughts \
-e JWT_SECRET=change-me \
-e BASE_URL=https://yourdomain.example.com \
-e NATS_URL=nats://nats:4222 \
thoughts
# Event worker (same image, different entrypoint)
docker run \
-e DATABASE_URL=postgres://postgres:password@db:5432/thoughts \
-e BASE_URL=https://yourdomain.example.com \
-e NATS_URL=nats://nats:4222 \
--entrypoint ./thoughts-worker \
thoughts
```
## License
MIT License. See [LICENSE](LICENSE).