Plateforme de streaming audio en temps reel avec backend Go et application mobile Flutter.
(English readers: a summary is provided at the end of this document.)
┌─────────────┐
│ Flutter App │
│ (iOS/Android)│
└──────┬──────┘
│ HTTP/SSE
┌──────▼──────┐
│ Go API │
│ (chi router)│
└──────┬──────┘
┌────────────┼────────────┐
│ │ │
┌─────▼─────┐ ┌───▼───┐ ┌─────▼─────┐
│ PostgreSQL │ │ Hub │ │ OTEL │
│ (DB) │ │(fan-out)│ │ Collector │
└───────────┘ └───────┘ └─────┬─────┘
│
┌────────▼────────┐
│ Prometheus/Grafana│
└─────────────────┘
domain/ Entites, interfaces (zero import externe)
application/ Use cases, services metier
infrastructure/ PostgreSQL, JWT, OTEL, Streaming Hub
transport/ Handlers HTTP, middlewares, DTOs
core/ Network (Dio), Storage, Utils
features/ auth, streams, playlists, favorites, admin
shared/ Widgets reutilisables
app/ Router, Theme, Constants
| Composant | Technologie |
|---|---|
| Backend | Go 1.26, chi, pgx, zerolog |
| Base de donnees | PostgreSQL 16 |
| Auth | JWT HS256 (bcrypt) |
| Streaming | SSE (fan-out Hub) |
| Mobile | Flutter 3.x, Riverpod, Dio |
| Observabilite | OpenTelemetry, Prometheus, Grafana |
| Contrat API | OpenAPI 3.1 (Redocly lint) |
| CI/CD | GitHub Actions |
| Conteneurisation | Docker, Docker Compose |
- Docker & Docker Compose v2+
- Go 1.26 (dev backend)
- Flutter 3.x (dev mobile)
# Secrets locaux (webhook Discord des alertes Grafana) - voir .env.example
cp .env.example .env
# puis remplir DISCORD_WEBHOOK_URL
# Lancer toute la stack
make up
# Verifier que l'API fonctionne
curl http://localhost:8080/health
# {"data":{"status":"ok"},"meta":{...}}Sans ce .env, la stack demarre quand meme mais les alertes Grafana n'ont nulle part ou notifier (webhook vide, echec silencieux).
| Service | URL |
|---|---|
| API | http://localhost:8080 |
| Grafana | http://localhost:3000 (admin/admin) |
| Prometheus | http://localhost:9090 |
| Tempo (traces) | http://localhost:3200 |
cd backend
cp .env.example .env
make run # Lancer le serveur
make test # Lancer les tests
make lint # Lintercd mobile
flutter pub get
flutter runLecture en arriere-plan et controles ecran verrouille via audio_service,
voir ADR 004.
| Livrable | Commande | Sortie |
|---|---|---|
| Binaire API | cd backend && make build |
backend/bin/server |
| Image Docker API | cd backend && make docker-build |
streampulse-api |
| APK Android | cd mobile && flutter build apk --release |
mobile/build/app/outputs/flutter-apk/ |
| AppBundle iOS (.ipa) | make ipa |
mobile/build/ios/ipa/StreamPulse-<version>+<build>.ipa |
| Console web | cd mobile && flutter build web --release -t lib/main_web.dart |
mobile/build/web/ |
make build-all enchaine binaire API + APK + .ipa.
Le .ipa n'est pas signe : c'est un livrable d'archive valide, mais il
doit etre re-signe avec un certificat de distribution pour s'installer sur un
appareil ou partir en TestFlight. Details : mobile/README.md.
Xcode est requis, donc le job CI correspondant tourne sur macos-latest.
Identifiant d'application : dev.streampulse.app (iOS et Android).
| Variable | Default | Description |
|---|---|---|
APP_ENV |
development | production durcit la configuration : le joker CORS est refuse au demarrage |
PORT |
8080 | Port du serveur |
DATABASE_URL |
- | URL PostgreSQL |
JWT_SECRET |
- | Secret JWT (changer en prod) |
JWT_EXPIRY |
15m | Duree access token |
JWT_REFRESH_EXPIRY |
168h | Duree refresh token |
OTEL_ENDPOINT |
localhost:4317 | Endpoint OTEL Collector |
LOG_LEVEL |
info | Niveau de log |
LOG_FORMAT |
json | Format de log : json (indexable) ou console (lisible en dev). Une valeur inconnue fait echouer le demarrage |
CORS_ALLOWED_ORIGINS |
* | Origines CORS, separees par des virgules. * n'est accepte qu'en dehors de APP_ENV=production ; avec le joker, Allow-Credentials n'est pas annonce |
RATE_LIMIT_RPS |
10 | Requetes/seconde par IP |
TRUSTED_PROXIES |
(vide) | Reverse-proxies dont X-Forwarded-For est accepte, en CIDR ou adresse. Vide = aucun en-tete de transmission n'est cru |
HTTP_READ_TIMEOUT |
30s | Lecture d'une requete (headers + corps) |
HTTP_WRITE_TIMEOUT |
30s | Ecriture d'une reponse |
HTTP_IDLE_TIMEOUT |
60s | Connexion keep-alive inactive |
TLS_CERT_FILE / TLS_KEY_FILE |
- | Renseignes ensemble, le serveur sert en HTTPS (TLS 1.2 min.) ; vides, il reste en clair derriere un reverse proxy, voir deployment.md |
PUBLIC_BASE_URL |
déduit | URL publique de l'API pour les liens des fichiers uploades ; vide = http(s)://localhost:PORT selon TLS |
REFRESH_TOKEN_PURGE_INTERVAL |
1h | Purge des refresh tokens expires (retention, voir rgpd.md) |
BROADCAST_GRACE_PERIOD |
10s | Delai avant l'arret automatique d'un direct dont le diffuseur a disparu (POST /broadcast termine sans remplacant) ; au demarrage, tout flux encore live est passe en ended |
Les routes de flux (/streams/{id}/listen, /audio, /broadcast) levent
ces timeouts pour leur propre connexion, voir ADR 005.
Le contrat REST est decrit en OpenAPI 3.1 : backend/api/openapi.yaml. C'est la source de verite unique — un test Go casse le build si le routeur et la description divergent.
| Ou | Quoi |
|---|---|
| backend/api/openapi.yaml | La description, dans le depot |
| http://localhost:8080/openapi.yaml | La meme, embarquee dans le binaire qui tourne |
| http://localhost:8080/docs | Swagger UI |
| docs/api.md | Guide narratif : conventions, flux d'auth, quickstart |
# Valider la description
cd backend && make openapi-lint
# Verifier qu'elle correspond aux routes reellement servies
cd backend && go test ./internal/transport/http/Endpoints principaux :
POST /auth/register- InscriptionPOST /auth/login- ConnexionGET /streams- Liste des streamsGET /streams/:id/listen- Ecouter un stream (SSE)GET /streams/:id/audio- Ecouter un stream (flux audio brut)POST /streams- Creer un stream (broadcaster)GET/POST/PUT/DELETE /playlists- CRUD playlists + file d'attenteGET /search- Recherche globale streams + musiquesGET/DELETE /users/me- Consulter et supprimer son propre compte (RGPD)
| Role | Permissions |
|---|---|
| user | Ecouter, playlists, favoris, consulter et supprimer son compte |
| broadcaster | + creer/gerer des streams |
| admin | + gestion des utilisateurs (roles, suppression) |
# Backend, suite unitaire (sans base)
cd backend && make test-unit
cd backend && make load-test # 1000 auditeurs sur le Hub + 500 clients SSE reels
cd backend && make bench # cout d'un chunk pour 10 a 10 000 auditeurs
# Backend, suite d'integration : repositories et API bout en bout contre PostgreSQL
export DATABASE_URL=postgres://localhost:5432/streampulse_test?sslmode=disable
make test-integration
# Tout, avec le seuil de couverture de la CI
make cover-check
# Mobile, avec le seuil de couverture de la CI
make test-mobile-coverCe qui est teste, a quel niveau et dans quel ordre : docs/plan-de-tests.md.
| Document | Pour quoi |
|---|---|
docs/api.md + /docs |
Le contrat REST, decrit en OpenAPI 3.1 |
| docs/user-stories.md | Fonctionnalites sous forme de user stories, tracees vers les cas d'usage et les tests |
| docs/diagrammes.md | Diagrammes UML et BPMN (cas d'usage, classes, sequences, etats, processus, composants) |
| docs/base-de-donnees.md | Modele conceptuel (MCD), modele physique et dictionnaire de donnees |
| docs/securite.md | Schema general de securite : zones de confiance, defense en profondeur, RBAC, OWASP Top 10 |
| docs/guide-utilisateur.md | Prise en main par role et plan de formation |
| docs/accessibilite.md | Utilisation en situation de handicap : lecteur d'ecran, taille du texte, contraste, limites connues |
| docs/accessible/ | La documentation technique complete en EPUB, plus le guide utilisateur et l'accessibilite en audio (make docs-accessible) |
| docs/performance.md | Fluidite de l'interface mesuree au profileur Flutter (DevTools / VM timeline), pas a l'oeil |
| docs/plan-de-tests.md | Plan de tests iteratifs : unitaires, integration, securite, cartographie des cas d'usage |
| docs/cahier-de-recette.md | 58 cas de recette executes |
| docs/slo.md | Objectifs de niveau de service et politique de budget d'erreur |
| docs/rgpd.md | Donnees personnelles : registre, retention, droits (acces, effacement), mesures de securite |
| docs/deployment.md | Deploiement |
| CHANGELOG.md | Historique des versions |
| docs/operations.md | Cycle de livraison, publication d'une version, boucle surveillance -> feuille de route |
- ADR 001 - Clean Architecture
- ADR 002 - Riverpod
- ADR 003 - SSE Streaming
- ADR 004 - Lecture en arriere-plan et session media
- ADR 004 - Observabilite : OTEL, Prometheus, logs correles
- ADR 005 - PostgreSQL et pgx sans ORM
- ADR 006 - JWT court et refresh token opaque
- ADR 007 - Effacement physique en cascade (RGPD)
- ADR 008 - Dashboard Grafana, traces distribuees et alertes
docs/scalability.md chiffre ce que la plateforme
encaisse et ou se situe le mur, a partir de mesures reproductibles
(make bench, make load-test).
En resume, a 100 flux simultanes et 50 auditeurs par flux : le reseau sature en premier (856 Mbit/s en SSE, soit 86 % d'une carte 1 Gbit/s) alors que le serveur tourne a 20 % de sa capacite. Le facteur limitant est la bande passante sortante, pas le code.
docs/performance.md mesure la fluidite de l'app mobile avec le profileur Flutter (timeline du VM Service, la meme source que Flutter DevTools), pas a l'oeil. En resume : le scroll d'une liste de 66 elements tient le budget de 16,67 ms (60 Hz) sur 99,3 % des images en mode debug (0 image en jank severe) ; le point chaud identifie n'est pas le scroll mais le rafraichissement periodique de la liste, qui reconstruit tout au lieu de ne mettre a jour que ce qui a change.
- Creer une branche depuis
develop - Commits conventionnels (
feat:,fix:,docs:) - Ouvrir une PR vers
develop - Review + CI verte requise
Projet realise a plusieurs, sans decoupage rigide backend/mobile : chacun est
intervenu sur les deux, avec un domaine de predilection. Repartition etablie
a partir de l'historique Git (git shortlog, contributions par dossier), pas
d'une attribution a posteriori.
| Contributeur | Domaine principal | Contributions notables |
|---|---|---|
| Jules Roche | Streaming & documentation | Hub de streaming SSE (infrastructure/streaming), handlers HTTP et repository PostgreSQL, feature streams cote mobile, provisioning Grafana/alerting ; redaction de la documentation (ADR 003 SSE, ADR 008 dashboard/alertes, plan de tests, RGPD, accessibilite, guide utilisateur) et de l'edition accessible EPUB/audio |
| Zeyoman (Alex) | Backend & CI/CD | Handlers HTTP, config et repositories PostgreSQL, RBAC et gestion des tokens ; features streams/playlists/music cote mobile ; pipelines CI (backend.yml, mobile.yml) ; ADR Riverpod, timeouts HTTP, effacement RGPD ; contrat OpenAPI et cahier de recette |
| Lilian Hammache (EkinL) | Architecture & authentification | Structuration Clean Architecture du backend, authentification (JWT, social login), observabilite (OTEL) ; feature auth cote mobile ; pipelines CI additionnels (security.yml, release.yml, dependabot) ; ADR Clean Architecture, choix PostgreSQL, strategie JWT, observabilite |
Le detail commit par commit reste consultable via git log --author="<nom>".
MIT
StreamPulse is a real-time audio streaming platform: a Go API (chi router,
Clean Architecture — domain/application/infrastructure/transport layers)
paired with a Flutter mobile app and web console, sharing one account
across three hierarchical roles (user < broadcaster < admin) — the app
requires an account for everything, though the API itself still answers a
handful of read routes without a token. Audio fans out from a broadcaster
to any number of listeners through an
in-memory Hub over goroutines and channels, served either as Server-Sent
Events (/listen) or a raw byte stream (/audio); JWT access tokens (15
min) pair with single-use opaque refresh tokens (168 h, bcrypt/SHA-256
hashed). PostgreSQL 16 via pgx (no ORM) persists users, streams,
playlists and favorites; OpenTelemetry, Prometheus and Grafana provide
tracing, metrics and dashboards, alerted to Discord. The REST contract is
normatively described in OpenAPI 3.1 (backend/api/openapi.yaml), checked
against the live router in CI.
make up starts the full stack; docs/ holds the complete technical
documentation — user stories, UML/BPMN diagrams, the conceptual and
physical data model, a security overview mapped to OWASP Top 10, the test
plan and executed acceptance cahier, SLOs, and an accessible EPUB/audio
edition of it all (docs/accessible/, make docs-accessible). Nine
Architecture Decision Records justify the major technical choices (Clean
Architecture, Riverpod, SSE over WebSocket, PostgreSQL, JWT strategy, GDPR
erasure, Grafana alerting). Scalability measurements
(docs/scalability.md) show the outbound network
saturates well before CPU at realistic load (856 Mbit/s of a 1 Gbit/s link
at 100 concurrent streams x 50 listeners, against 20% server capacity
used) — the platform's real ceiling is bandwidth, not code.
Team project with no rigid backend/mobile split — everyone touched both
sides, each with an area of focus. Breakdown derived from Git history
(git shortlog, per-directory contributions), not assigned after the fact.
| Contributor | Main area | Notable contributions |
|---|---|---|
| Jules Roche | Streaming & documentation | SSE streaming Hub (infrastructure/streaming), HTTP handlers and PostgreSQL repositories, mobile streams feature, Grafana/alerting provisioning; wrote most of the documentation (ADR 003 SSE, ADR 008 dashboard/alerting, test plan, GDPR, accessibility, user guide) and the accessible EPUB/audio edition |
| Zeyoman (Alex) | Backend & CI/CD | HTTP handlers, PostgreSQL config and repositories, RBAC and token handling; mobile streams/playlists/music features; CI pipelines (backend.yml, mobile.yml); ADRs for Riverpod, HTTP timeouts, GDPR erasure; OpenAPI contract and acceptance test book |
| Lilian Hammache (EkinL) | Architecture & authentication | Clean Architecture structuring of the backend, authentication (JWT, social login), observability (OTEL); mobile auth feature; additional CI pipelines (security.yml, release.yml, dependabot); ADRs for Clean Architecture, PostgreSQL choice, JWT strategy, observability |
Commit-level detail remains available via git log --author="<name>".