#71 — rapport-arcep
PLANIFIÉ
Priorité: 🔴 CRITIQUE · Type: C (Celery) · Conteneur: rgz-beat · Code: app/tasks/arcep.pyDépendances: #47 arcep-reporting, #10 rgz-beat
Description
Rapport de conformité réglementaire ARCEP (Autorité de Régulation de la Poste et des Communications, Bénin) généré automatiquement tous les trimestres. Non-négociable pour la légalité du réseau RGZ.
Ce rapport consolide les données obligatoires pour un opérateur télécomunications FAI au Bénin :
- Abonnés uniques (COUNT DISTINCT subscriber_id)
- Volume de données (sum bytes_in+out par trimestre, en To)
- Couverture géographique (8 villes principales avec % uptime par site)
- Incidents > 1h (P0/P1 avec timeline, cause, résolution)
- Traçabilité de sécurité : MAC address + MSISDN + timestamp + NAS-ID (immuable SHA-256)
Généré en PDF selon template ARCEP officiel, avec dossier complet (sécurité + incidents + SLA). Soumission via API ARCEP portail (ou email backup si API indisponible).
Impact légal: Violation délai → amende 10M FCFA + suspension licence. Priorité CRITIQUE.
Architecture Interne
Flux de Génération Trimestrielle
Every Q1/Q2/Q3/Q4 transition:
↓
Celery Beat déclenche rgz.arcep.quarterly
↓
Vérifier trimestre (Q4 = 01/01, Q1 = 01/04, Q2 = 01/07, Q3 = 01/10)
↓
Générer dossier conformité (3-4 fichiers PDF):
1. arcep_q{N}_{year}_summary.pdf (synthèse 10 pages)
2. arcep_q{N}_{year}_security.pdf (traçabilité MAC)
3. arcep_q{N}_{year}_incidents.pdf (détail P0/P1)
4. arcep_q{N}_{year}_sites.pdf (coverage par ville)
↓
Signature SHA-256 + timestamp
↓
Upload ARCEP Portal API v2 (oauth2 credentials)
↓
Sauvegarde locale + logs immutables (#45)
↓
Email confirmation NOCSchéma de Données
-- Tables source (creées ailleurs)
TABLE subscribers:
id UUID | subscriber_ref | msisdn | status | created_at
TABLE radius_sessions:
id UUID | subscriber_id | bytes_in BIGINT | bytes_out BIGINT | session_start | session_stop
TABLE incidents:
id UUID | priority | title | created_at | resolved_at | description
TABLE subscriber_devices:
subscriber_id UUID | mac_address TEXT | first_seen_at TIMESTAMP
-- Table de tracking (créée par #71)
TABLE arcep_quarterly_submissions:
id UUID PK
year INT
quarter INT (1-4)
submission_date TIMESTAMP
dossier_path TEXT (ex: /arcep/2026/q1/)
summary_pdf TEXT (ex: arcep_q1_2026_summary.pdf)
security_pdf TEXT (ex: arcep_q1_2026_security.pdf)
incidents_pdf TEXT
sites_pdf TEXT
submission_status CHECK(draft|submitted|accepted|rejected|failed)
arcep_reference_number TEXT (après acceptation)
submission_hash_sha256 TEXT
signed_at TIMESTAMP
UNIQUE(year, quarter)Contenu PDF 1 : Summary (10 pages)
ARCEP — RAPPORT TRIMESTRIEL
RGZ S.A. — Opérateur FAI Bénin
Trimestre: Q1 2026 (01/01 — 31/03/2026)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. SYNTHÈSE EXÉCUTIVE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Abonnés actifs (end of quarter): 487
Volume data (trimestre): 12.3 To (DL 8.9, UL 3.4)
ARPU moyen: 45,000 FCFA
Uptime global: 99.7%
Incidents > 1h (P0/P1): 2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2. STATISTIQUES PAR RÉSEAU
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Revendeur Abonnés Volume(GB) Uptime Incidents
─────────────────────────────────────────────────────────────
Tech Connect 142 3,240 99.9% 0
Kossou WiFi 98 2,150 99.5% 1
Digital Benin 65 1,890 99.8% 0
Abomey Connect 45 980 99.2% 1
[...]
TOTAL 487 12,260 99.7% 2
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3. COUVERTURE GÉOGRAPHIQUE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Cotonou (principal): Uptime 99.9%, 234 abonnés
Abomey: Uptime 99.6%, 87 abonnés
Parakou: Uptime 99.4%, 56 abonnés
Lokoja: Uptime 99.8%, 45 abonnés
Natitingou: Uptime 99.1%, 32 abonnés
Ouidah: Uptime 99.7%, 22 abonnés
Tchetti: Uptime 99.5%, 11 abonnés
Porto-Novo: Uptime 99.8%, 28 abonnés
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
4. CONFORMITÉ RÉGLEMENTAIRE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Traçabilité AAA complète (RADIUS logs)
✓ SHA-256 immutable logs (12 mois)
✓ Sauvegardes quotidiennes (3x30j)
✓ DNSSEC enabled
✓ Encryption TLS 1.3
✓ Incidents documentés (RCA disponible)
✓ Pas de bascules non autorisées
✓ Pas de fuites données client
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5. INCIDENTS > 1h (DETAILS COMPLETS EN PDF 3)
01/15 — Perte BGP (P0)
Durée: 25 minutes (< 1h, INFO seulement)
[Aucun incident >= 1h ce trimestre]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
6. DÉCLARATION D'EXACTITUDE
Je certifie sur l'honneur que les données ci-dessus sont exactes
et conformes à nos logs immuables, audités régulièrement.
PDG : [Signature électronique e-signing.bj]
Date: 2026-04-01Contenu PDF 2 : Security (Traçabilité)
ARCEP — TRAÇABILITÉ SECURITY
RGZ S.A. — Q1 2026
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LOGS IMMUABLES (12 DERNIERS MOIS)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Format: MSISDN | MAC Address | NAS-ID | Timestamp | Bytes In | Bytes Out
0197979964 | AA:BB:CC:DD:EE:FF | access_tech_connect_s1 | 2026-01-15T14:30:42Z | 512000 | 256000
0197979965 | 11:22:33:44:55:66 | access_kossou | 2026-01-15T14:35:10Z | 768000 | 384000
[... 10,000s de lignes]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
INTÉGRITÉ SHA-256
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Logs Period: 2025-01-15 — 2026-01-15
Total Entries: 487,234
Hash SHA-256: f7e42c9f5a3e4b2d8c1f6a9e2b4d7c8f (immutable storage)
Last Entry Hash: 3d4f5e6a7b8c9d0e1f2a3b4c5d6e7f8a
Merkle Root: abc123def456...
Verification: ✓ PASSED (audited 2026-01-20)Contenu PDF 3 : Incidents
ARCEP — DÉTAIL INCIDENTS > 1h
RGZ S.A. — Q1 2026
[Ce trimestre: 0 incident >= 1h]
Historique P0/P1 < 1h (reportage optionnel):
2026-01-15 — 14:30 Perte BGP route (P0)
Durée: 25 minutes
Cause: Configuration erreur NAS-Cotonou switch
Impact: ~50 abonnés sans accès
Résolution: Bascule route secondaire (auto)
RCA: /api/v1/incidents/incident-id/rcaConfiguration
# ARCEP Reporting
ARCEP_SUBMISSION_ENABLED=true
ARCEP_API_ENDPOINT=https://arcep-portal.bj/api/v2/submissions
ARCEP_CLIENT_ID=rgz-sa-12345
ARCEP_CLIENT_SECRET=***
ARCEP_OAUTH_TOKEN_URL=https://arcep-portal.bj/oauth/token
# Trimestres : Q1=01/04, Q2=01/07, Q3=01/10, Q4=01/01
ARCEP_Q1_DEADLINE=04/10 # 10 jours avant fin Q2
ARCEP_Q2_DEADLINE=07/10
ARCEP_Q3_DEADLINE=10/10
ARCEP_Q4_DEADLINE=01/10
# Archivage
ARCEP_DOSSIER_PATH=/arcep/{year}/q{quarter}/
ARCEP_RETENTION_YEARS=10 # Conservation légale
# Contact
ARCEP_CONTACT_NAME=Directeur Conformité
ARCEP_CONTACT_EMAIL=compliance@rgz.bj
ARCEP_CONTACT_PHONE=+229 97 97 97 97Endpoints API
| Méthode | Route | Description | Réponse |
|---|---|---|---|
| GET | /api/v1/reports/arcep/auto?quarter=Q1&year=2026 | Générer automatiquement | 202 Accepted + |
| GET | /api/v1/reports/arcep/{year}/{quarter} | Récupérer rapport Q | |
| GET | /api/v1/reports/arcep/{year}/{quarter}/pdf | Télécharger dossier ZIP | ZIP binary (4 PDF) |
| POST | /api/v1/reports/arcep/{year}/{quarter}/submit | Soumettre ARCEP portal | 202 Accepted |
| GET | /api/v1/reports/arcep/{year}/{quarter}/status | Statut soumission ARCEP |
Authentification: Admin only (rôle compliance)
Celery Task
| Champ | Valeur |
|---|---|
| Task name | rgz.arcep.quarterly |
| Schedule | Cron 0 2 1 4,7,10,1 * (01/04, 01/07, 01/10, 01/01 à 02:00 UTC) |
| Queue | rgz.reports |
| Timeout | 1800s (30 minutes) |
| Retry | 5x (backup email si API ARCEP down) |
Code Celery esquisse:
@app.task(name='rgz.arcep.quarterly', bind=True)
def generate_arcep_quarterly(self):
"""
Génère dossier ARCEP trimestriel complet
"""
quarter, year = _get_current_quarter()
try:
# 1. Récupérer données période
period_start = _quarter_start_date(quarter, year)
period_end = _quarter_end_date(quarter, year)
# 2. Générer 4 PDF en parallèle
tasks = [
_generate_arcep_summary_pdf.delay(quarter, year),
_generate_arcep_security_pdf.delay(quarter, year),
_generate_arcep_incidents_pdf.delay(quarter, year),
_generate_arcep_sites_pdf.delay(quarter, year)
]
pdf_paths = [task.get() for task in tasks]
# 3. Créer dossier + SHA-256
dossier_path = f"/arcep/{year}/q{quarter}/"
os.makedirs(dossier_path, exist_ok=True)
for pdf in pdf_paths:
shutil.copy(pdf, dossier_path)
dossier_hash = _compute_dossier_sha256(dossier_path)
# 4. Enregistrer dans DB
submission = ArcepQuarterlySubmission(
year=year,
quarter=quarter,
submission_date=datetime.utcnow(),
dossier_path=dossier_path,
submission_hash_sha256=dossier_hash,
submission_status='draft'
)
db.add(submission)
db.commit()
# 5. Soumettre ARCEP portal (ou email backup)
try:
arcep_ref = _submit_to_arcep_portal(submission)
submission.submission_status = 'submitted'
submission.arcep_reference_number = arcep_ref
except requests.HTTPError:
logger.error("ARCEP portal unavailable, sending email backup")
_email_arcep_backup(submission)
submission.submission_status = 'email_backup'
db.commit()
return {'status': 'success', 'submission_id': str(submission.id)}
except Exception as e:
logger.error(f"ARCEP generation failed: {e}")
self.retry(exc=e, countdown=3600) # Retry in 1hCommandes Utiles
# Déclencher génération manuelle Q1 2026
docker-compose exec rgz-api celery -A app.celery_app call rgz.arcep.quarterly
# Vérifier statut soumission ARCEP
curl -H "Authorization: Bearer {admin_token}" \
"http://api-rgz.duckdns.org/api/v1/reports/arcep/2026/q1/status"
# Télécharger dossier ARCEP complet
curl -H "Authorization: Bearer {admin_token}" \
"http://api-rgz.duckdns.org/api/v1/reports/arcep/2026/q1/pdf" \
-o arcep_q1_2026.zip && unzip arcep_q1_2026.zip
# Vérifier intégrité dossier
sha256sum /arcep/2026/q1/arcep_q1_2026_summary.pdf
# Comparer avec valeur DB: SELECT submission_hash_sha256 FROM arcep_quarterly_submissions WHERE year=2026 AND quarter=1
# Logs soumission ARCEP
docker-compose logs rgz-beat | grep "arcep"
# Archiver anciens dossiers (> 10 ans légalement pas nécessaire mais conservé)
find /arcep -maxdepth 2 -type d -mtime +3650 -name "q*" | head -5Implémentation TODO
- [ ] Schéma DB
arcep_quarterly_submissions(year, quarter, status, hash, arcep_ref) - [ ] Tâche Celery
rgz.arcep.quarterlydansapp/tasks/arcep.py - [ ] Templates Jinja2 4 PDF (summary, security, incidents, sites)
- [ ] Integration OAuth2 client pour ARCEP portal API
- [ ] Fonctions de génération PDF:
_generate_arcep_*_pdf() - [ ] Validation données (abonnés, volumes, incidents)
- [ ] Endpoints API GET/POST /api/v1/reports/arcep*
- [ ] Backup email si ARCEP portal down
- [ ] Signature électronique e-signing (e-signature.bj ou PreSigno)
- [ ] Tests: données Q1/Q2/Q3/Q4 synthétiques
- [ ] Monitoring: alerte si soumission pas envoyée avant deadline
- [ ] Audit trail #48 : tracer chaque submission
- [ ] Documentation: processus légal ARCEP, deadlines, contacts
Dernière mise à jour: 2026-02-21