Architecture
Suddenly est une application Django standard qui tourne partout où Python et PostgreSQL sont disponibles. Docker est optionnel.
Stack technique
| Composant | Technologie | Rôle |
|---|---|---|
| Backend | Python 3.11+ + Django 5.0 | Application principale (SSR) |
| API REST | Django REST Framework | API + OpenAPI (drf-spectacular) |
| Auth | django-allauth | Authentification |
| Frontend | HTMX + Alpine.js + UnoCSS | UI sans SPA (~32KB total) |
| Fédération | ActivityPub custom | Protocole de fédération |
| Signatures | HTTP Signatures (RSA-SHA256) | Authentification des requêtes AP |
| Base de données | PostgreSQL 16 | Datastore principal |
| Cache / Queue | Redis 7 (optionnel) | Broker Celery + cache |
| Workers | Celery + django-celery-beat | Tâches asynchrones |
Flux de services
Browser ──HTMX/JSON──► Django App ──ORM──► PostgreSQL
│
Remote Instance ──AP──► │ ──enqueue──► Celery Worker
│ │
└──────────────► Redis (optionnel)
ou DB cache (fallback)
Matrice de dépendances
| Composant | Requis | Optionnel | Fallback |
|---|---|---|---|
| Python 3.11+ | ✓ | — | — |
| PostgreSQL | ✓ | — | — |
| Redis | — | ✓ | DB cache |
| Celery | — | ✓ | Tâches synchrones |
Profils de déploiement
- Petite instance (< 50 utilisateurs) — Django + PostgreSQL uniquement
- Instance moyenne (50–500) — + Redis recommandé
- Grande instance (500+) — + Celery pour la livraison fédérée asynchrone
Fédération ActivityPub
Endpoints exposés
| Endpoint | Protocole | Description |
|---|---|---|
/.well-known/webfinger | WebFinger | Découverte des acteurs |
/.well-known/nodeinfo | NodeInfo | Métadonnées de l'instance |
/users/{id}/inbox | ActivityPub | Réception des activités |
/users/{id}/outbox | ActivityPub | Publication des activités |
Sécurité des requêtes
Toutes les requêtes ActivityPub sortantes sont signées avec RSA-SHA256. Les en-têtes signés sont : (request-target) host date digest.
Les clés ne se génèrent pas à la main : Suddenly les crée lui-même.
- Clés d'instance —
ensure_instance_keys()écritkeys/private.pem(chmod 0600) etkeys/public.pems'ils sont absents. C'est ce que fait./scripts/init-dev.shen local. - Clés d'acteur — chaque utilisateur, personnage et partie reçoit sa propre paire RSA à la création, via signal Django. Rien à faire.
Rate limiting
| Surface | Limite | Où |
|---|---|---|
| Inbox AP — instance connue | 100 req/min par domaine | django-ratelimit |
| Inbox AP — instance inconnue | 10 req/min par domaine | django-ratelimit |
Connexion (POST /accounts/login/) | 10 tentatives/min par IP | middleware Django |
Inscription (POST /accounts/signup/) | 5 tentatives/min par IP | middleware Django |
L'API REST n'est pas limitée à ce jour : aucune classe de throttling DRF n'est configurée. Si vous exposez l'API publiquement, prévoyez une limitation en amont (nginx, WAF).
Structure du projet
suddenly/
├── config/settings/
│ ├── base.py # Paramètres partagés
│ ├── development.py # Overrides dev
│ └── production.py # Production (sécurité renforcée, env requis)
├── config/asgi.py
├── suddenly/wsgi.py # Point d'entrée WSGI (pas config/wsgi.py)
├── manage.py
├── pyproject.toml # Dépendances (pip install -e ".[federation]")
├── docker-compose.yml
├── docker-compose.dev.yml
├── scripts/ # install.sh, entrypoint.sh, backup.sh, restore.sh…
├── staticfiles/ # Static collectés (whitenoise)
└── media/ # Uploads utilisateurs
Le module WSGI est
suddenly/wsgi.py— le paquetconfig/ne contient que les settings et l'ASGI. Une commandegunicorn config.wsgi:applicationéchouera.
Qualité et CI
- Lint : Ruff (règles E, F, I, N, W, UP)
- Type check : mypy strict + django-stubs
- Tests : pytest + pytest-cov, seuil de couverture 50%
- CI : GitHub Actions — ruff + mypy + pytest --cov, bloque les merges en cas d'échec
- Pre-commit : ruff --fix + mypy
Lancer tous les checks en local :
make check