Spec — Backend d'ingestion audio (Skald v0.1)
Documentation écrite avant le code (CLAUDE.md §2.2). Pas de TDD (décision mainteneur 2026-05-17), vérification manuelle par
curl.Approche retenue (validée) : B — stack cible, compatible Homelab. Symfony 7 + API Platform 4 + Doctrine + PostgreSQL 16 (pgvector), conteneurisé FrankenPHP, déployable sur le homelab Phracktale.
1. Objectif
Exposer une API REST qui reçoit les enregistrements audio du device Skald, stocke le fichier et ses métadonnées, et sert de socle au pipeline de traitement (transcription, etc., phases ultérieures).
Périmètre v0.1 : ingestion uniquement (réception + persistance). Pas de traitement IA, pas d'app mobile, pas d'authentification (voir §6).
2. Architecture (rappel cible + intégration Homelab)
Device Skald ──HTTPS──> Heimdall (192.168.1.195, Nginx + Let's Encrypt)
│ api.skald.phracktale.com
▼
Thor (192.168.1.36)
homelab_skald_app (FrankenPHP/Symfony, Caddy TLS interne)
homelab_skald_db (pgvector/pgvector:pg16, LAN only)
Conventions Homelab respectées (cf. HOMELAB/_SERVICES/CLAUDE.md,
port-routing-table.md, pattern FrankenPHP) :
| Élément | Valeur |
|---|---|
| Domaine public | api.skald.phracktale.com |
| Dossier service | HOMELAB/_SERVICES/api.skald.phracktale.com/ |
| Conteneur app | homelab_skald_app |
| Conteneur DB | homelab_skald_db |
| Volume DB | homelab_skald_db_data |
| Réseau | homelab_skald_network |
| Port HTTP (Thor) | 18090 |
| Port HTTPS (Thor) | 18453 (= 18090 + 363, convention paire) |
| Port Postgres (Thor) | 18092 — bind 192.168.1.36 (LAN only) |
| TLS interne | Caddy/FrankenPHP (CADDY_TLS=internal en dev, vide en prod) |
| Secrets | .env.example commité ; .env généré sur Thor par bootstrap.sh |
restart |
unless-stopped |
Ports choisis libres dans la plage 18xxx (vérifié dans
port-routing-table.md : 18080-18082, 18173, 18200, 18300-18303, 18443,
18445 déjà pris ; 18090/18453/18092 libres).
3. Modèle de données
Entité Doctrine Recording (table recording) :
| Champ | Type | Note |
|---|---|---|
id |
UUID | clé primaire |
originalFilename |
string | nom fourni par le client |
storagePath |
string | chemin relatif du fichier stocké |
mimeType |
string | ex. audio/wav, audio/ogg |
sizeBytes |
int | taille du fichier |
deviceId |
string nullable | identifiant device (optionnel v0.1) |
recordedAt |
datetime nullable | horodatage capture (optionnel) |
uploadedAt |
datetime | rempli serveur |
status |
enum | received (v0.1) ; valeurs futures : processing, done |
pgvector est installé dès maintenant (extension vector) mais aucune
colonne d'embedding en v0.1 — préparé pour la recherche sémantique (Phase 2).
4. Contrat d'API
Ressource API Platform Recording, endpoint d'upload :
POST /api/recordings—multipart/form-datafile(requis) : le fichier audiodeviceId(optionnel) : stringrecordedAt(optionnel) : ISO 8601- Réponses :
201 Created+ JSON de la ressource (id,originalFilename,mimeType,sizeBytes,uploadedAt,status)400sifileabsent415si type MIME non audio
GET /api/recordings— liste paginée (lecture, vérif manuelle)GET /api/recordings/{id}— détailGET /api/docs— documentation OpenAPI/Swagger (API Platform)GET /health—{"status":"ok"}(sonde Heimdall/healthcheck Docker)
Stockage fichier : volume monté, chemin
var/uploads/<uuid>.<ext> (le dossier var/ est déjà gitignoré côté skald).
Limites PHP à relever pour l'audio long :
upload_max_filesize / post_max_size (documenté dans le Dockerfile/php.ini).
5. Implémentation (sans test)
- Scaffolding Symfony 7 (
symfony new skald/backend --webappou squelette API + composer require api-platform). composer require api-platform/core doctrine+ configDATABASE_URLPostgreSQL.- Entité
Recording+ migration Doctrine ; activation extensionvector. - Endpoint d'upload : un state processor API Platform (ou contrôleur dédié) qui valide le fichier, le stocke, persiste l'entité.
- Endpoint
/health. - Dockerfile FrankenPHP +
compose.yaml(app + db pgvector) +.env.example. - Artefacts Homelab dans
HOMELAB/_SERVICES/api.skald.phracktale.com/(compose, nginx vhost, scripts bootstrap/deploy, CLAUDE.md de service). - Mise à jour
HOMELAB/.../port-routing-table.md(entrée Skald). - Vérif manuelle locale :
docker compose up,curlupload +GET.
6. Sécurité v0.1 (décisions à acter)
- Authentification : Bearer token sur tout
/api/*(en-têteAuthorization: Bearer <SKALD_API_TOKEN>), viaBearerTokenSubscriber./healthreste public (sonde). Secret partagé unique, rotable par l'env sans redéploiement de code. Fail-closed si le token n'est pas configuré. Évolution possible vers une auth par device (cf.SECURITY.md). - Postgres : binding
192.168.1.36(LAN only), jamais exposé Internet. - Secrets (
POSTGRES_PASSWORD,APP_SECRET) générés sur Thor parbootstrap.sh(openssl rand), jamais commités. - Le fichier
.envréel vit uniquement sur Thor.
7. Répartition deux dépôts
| Dépôt | Contenu |
|---|---|
combadge (skald) |
code Symfony (skald/backend/), cette spec |
HOMELAB |
_SERVICES/api.skald.phracktale.com/ (compose, nginx, scripts, CLAUDE.md), maj port-routing-table.md |
Toute modification du dépôt HOMELAB est une action structurante sur un autre projet : validation explicite du mainteneur avant d'écrire dedans.
8. Sources
HOMELAB/_SERVICES/CLAUDE.md(conventions services)HOMELAB/.../docs/infra/port-routing-table.md(allocation ports)HOMELAB/.../docs/infra/homelab-minimal-php-symfony-frankenphp-tutorial.mdHOMELAB/_SERVICES/api.buvette.phracktale.com/(service de référence)_DOCS/contexte.md§5 (stack cible : Symfony 7, API Platform, pgvector)