Plateforme ouverte
Développeurs
Une API publique en lecture seule, des webhooks signés, des manifestes vérifiables hors ligne et un widget intégrable. Tout ce qu’un tiers peut lire est déjà public sur le site : jamais un participant, un bénévole, un message ni un compte.
API publique
L’API publique est sous /api/public/v1 : stable, versionnée, en lecture seule. Mouvements, versions de revendications et de charte, événements à venir, affiliations, finances publiées, vérification d’empreintes.
- Lecture seule : GET, HEAD et OPTIONS ; toute écriture répond 405.
- Données publiques uniquement : ce qui est visible sur le site, ni plus ni moins.
- CORS ouvert en lecture, sans cookie ni identifiants : un navigateur appelle l’API sans clé.
- Pagination par curseur (?limit= et ?cursor=), listes triées par date, jamais par popularité ; aucun total.
- ?lang=fr|en : traduction validée et à jour si elle existe, sinon langue source. Les empreintes restent celles de la langue source.
- ETag et If-None-Match (réponse 304) ; Cache-Control public, max-age=60 ; un contenu retiré disparaît de l’origine immédiatement.
Obtenir une clé
Sans clé, le quota est faible. Une clé donne un quota plus élevé, sans aucun accès supplémentaire. Un administrateur de mouvement crée ses clés dans « Clés d’API » de l’espace du mouvement ; un compte d’organisation vérifié, dans « Organisations ». Le secret n’est affiché qu’une fois.
- Jamais dans l’adresse d’une requête : ?api_key= est refusé (400). Utilisez Authorization: Bearer ak_live_… ou X-Api-Key.
- Jamais dans une page ou une application servie au navigateur : une clé est faite pour un serveur.
- 365 jours au plus, 10 clés utilisables par propriétaire, renouvellement avec chevauchement jusqu’à 72 heures, révocation immédiate. La clé d’un mouvement cesse de fonctionner si son créateur n’en est plus administrateur.
Quotas
| Appelant | Quota par défaut | Compteur |
|---|---|---|
| Anonyme | 60 requêtes par minute | Adresse IPv4 ou préfixe IPv6 /64 |
| Avec une clé | 600 requêtes par minute (6 000 au plus) | La clé |
Les routes coûteuses pèsent plus : le manifeste d’un mouvement compte 10 unités, le widget (embed) et la vérification avec un mouvement 5, le reste 1. Un plafond global protège chacune (429 public_route_busy) et les adresses IPv6 d’un même /48 partagent un budget.
Chaque réponse porte l’état du quota dans les en-têtes RateLimit-* :
RateLimit-Limit: 600
RateLimit-Remaining: 597
RateLimit-Reset: 41
RateLimit-Policy: 600;w=60
X-Arallia-Api-Version: v1Au-delà, 429 rate_limited avec Retry-After (secondes) : respectez-le et ne réessayez pas un 4xx (sauf 429).
Exemples
curl -s "https://arallia.fr/api/public/v1/movements/climat-rhone/claims?lang=en"curl -s "https://arallia.fr/api/public/v1/events?near=45.76,4.84&radius_km=50&limit=20" \
-H "Authorization: Bearer ak_live_0123456789_<64 hex>"curl -si "https://arallia.fr/api/public/v1/movements/climat-rhone" -H 'If-None-Match: W/"0a1b…"'Versionnement
Le préfixe /v1 est la version. Les changements additifs (champ, route, valeur d’énumération) n’en changent pas : tolérez les champs et valeurs inconnus. Un changement incompatible crée /v2 ; /v1 reste servie au moins 6 mois, avec les en-têtes Deprecation puis Sunset et un lien successor-version. Chaque réponse porte X-Arallia-Api-Version.
Schéma OpenAPI de l’API publique (JSON)
L’API ne fournit ni classement ni score : n’en fabriquez pas. Un pack juridique non vérifié ou une affiliation non confirmée doit rester signalé comme tel.
Webhooks
Un administrateur du mouvement abonne une adresse https à des événements du mouvement, depuis « Webhooks » de son espace. ARALLIA envoie un POST JSON signé.
Types : claims_version_published, charter_version_published, event_published, event_updated, event_cancelled, event_reinstated, official_post_published, alert_published, alert_retracted, proposal_adopted, proposal_executed (et ping pour les tests).
{
"id": "0192f3e0-7c1a-7e44-9a0b-6d1e2f3a4b5c",
"type": "event_cancelled",
"created_at": "2026-10-05T09:12:44Z",
"movement": { "id": "…", "slug": "climat-rhone" },
"data": { "event_id": "…", "event_slug": "marche-pour-le-climat-lyon" }
}Le corps est minimal : identifiants et données publiques. Il dit « quelque chose a changé » ; relisez l’API publique pour le contenu. Pas d’ordre garanti entre événements : utilisez created_at.
En-têtes
X-Arallia-Event: event_cancelled
X-Arallia-Delivery: 0192f3e0-7c1a-7e44-9a0b-6d1e2f3a4b5c
X-Arallia-Signature: t=1760000000,v1=9f2c…e1,v1=4b7a…03
User-Agent: Arallia-Webhooks/1Vérifier la signature (obligatoire)
- Lisez le corps brut, avant tout re-formatage JSON.
- Extrayez t et chaque v1 de l’en-tête X-Arallia-Signature (t=[secondes Unix],v1=[hex]).
- Refusez si la différence entre maintenant et t dépasse 300 secondes : c’est la protection contre le rejeu.
- Calculez HMAC-SHA256(secret, t + "." + corps) en hexadécimal et comparez-le à chaque v1 en temps constant.
- Dédupliquez sur X-Arallia-Delivery, identique à chaque tentative : une livraison peut être reprise.
import hmac, hashlib, time
def verify(header: str, body: bytes, secret: str, tolerance: int = 300) -> bool:
parts = [p.split("=", 1) for p in header.split(",")]
t = int(next(v for k, v in parts if k == "t"))
if abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, v) for k, v in parts if k == "v1")Rotation du secret
À la rotation, l’ancien secret reste valable 24 heures par défaut (0 à 72 heures) : pendant ce temps, l’en-tête porte deux signatures v1. Acceptez-les toutes deux, déployez le nouveau secret, puis seul le nouveau subsiste.
Livraison et reprises
- Répondez par un code 2xx en moins de 5 secondes. Tout autre code (redirections 3xx comprises, non suivies), un délai dépassé ou une erreur de connexion est un échec.
- 8 tentatives sur 22 h 36 (après 1 min, 5 min, 30 min, 1 h, 3 h, 6 h, 12 h), puis abandon. Une seule livraison à la fois par destinataire.
- Après 10 échecs consécutifs, l’abonnement est désactivé et l’équipe est notifiée ; réactivation après correction.
- Adresse : https, nom de domaine public (ni IP, ni localhost, ni nom local), sans identifiants, port 443, résolue vers des adresses publiques à chaque livraison. Une adresse refusée est signalée « URL non autorisée ».
- Test : le bouton « Envoyer un test » envoie un ping signé et affiche le résultat ; le journal garde 30 jours de livraisons.
Vérifier les signatures
La plateforme signe en Ed25519 l’empreinte du manifeste de chaque mouvement (arallia-manifest-4), l’ensemble de ses événements et celui de ses traductions validées. La vérification se fait hors ligne, sans nous croire sur parole.
Commencez par épingler la clé : relevez l’empreinte SHA-256 de la clé de signature depuis une source indépendante de ce site (annonce de la plateforme, dépôt public, DNS) et refusez toute autre clé. Sans épinglage, vous ne pouvez conclure qu’à la cohérence du document (valid_unpinned), jamais à son origine.
- Récupérez le manifeste signé (…/manifest) et les clés (/.well-known/arallia-keys.json, revalidé à chaque lecture).
- Recalculez le SHA-256 du JSON canonique du manifeste et comparez-le à sha256 et à signature.manifest_hash.
- Retrouvez la clé par key_id, refusez-la si elle est révoquée, et exigez que son empreinte soit épinglée.
- Vérifiez la signature Ed25519 du message v2 (schéma, empreinte, key_id et signed_at) et que signed_at tombe dans la validité de la clé.
- Faites de même pour les événements (leur absence rend le document partiel) et pour les traductions si elles sont présentes.
# 1. Récupérer le manifeste signé et les clés publiques
curl -s https://<site>/api/public/v1/movements/<mouvement>/manifest -o manifeste.json
curl -s https://<site>/.well-known/arallia-keys.json -o arallia-keys.json
# 2. Vérifier hors ligne, en épinglant l'empreinte de clé publiée HORS du site
arallia-admin verify-manifest manifeste.json --keys arallia-keys.json --pin <empreinte-sha256>
# code 0 : valide ET clé épinglée | 3 : valide mais clé non épinglée | 4 : partiel | 1 : invalide | 2 : non signéÉtats : valid (vérifiée et clé épinglée) est le seul qui permette d’écrire « signé » ; valid_unpinned, stale, unsigned, invalid, key_revoked, unknown_key et partiel n’y autorisent jamais. La signature atteste la publication, pas la véracité des revendications.
Widget
Le widget est une page ARALLIA (/embed/[mouvement]) que vous intégrez dans un iframe. Elle affiche les revendications officielles courantes, leur version, l’état de signature et les prochains événements.
- Le mouvement active le widget et déclare les origines autorisées ; une page dont l’origine n’est pas dans la liste ne peut pas l’afficher (en-tête frame-ancestors).
- Aucune action authentifiée, aucun cookie : « Je participe » est un lien qui ouvre le site ARALLIA dans un nouvel onglet.
- Options : ?lang=fr|en et ?theme=light|dark (sinon, selon le système du visiteur).
<iframe src="https://arallia.fr/embed/mon-mouvement?lang=fr&theme=light"
title="Revendications de Mon mouvement" width="100%" height="520"
loading="lazy" referrerpolicy="no-referrer" style="border:0"></iframe>