API v1
Base : https://eclair.kayzen-lyon.com/api/v1. JSON partout, réponses jamais mises en cache. Les données binaires sont encodées en base64url sans remplissage. openapi.json
Règle d'or : chiffrez avant d'appeler l'API. N'envoyez jamais la clé du lien, le code PIN ni le contenu en clair. Le plus simple est d'utiliser notre client JavaScript (paquet api-client) ou la CLI.
Routes
| Méthode | Route | Rôle |
|---|---|---|
POST | /secrets | Stocker un secret déjà chiffré |
POST | /secrets/{id}/preview | Libellé chiffré et indicateurs, sans le contenu |
POST | /secrets/{id}/reveal | Révéler (consomme une lecture) |
GET | /manage/{id} | Statut et historique (accusé de lecture) |
DELETE | /manage/{id} | Détruire avant lecture |
POST | /uploads | Ouvrir un envoi de fichiers chiffrés |
POST | /uploads/{id}/parts | URL présignée (5 min) pour une partie |
POST | /requests | Créer une demande de secret (lien de dépôt) |
POST | /requests/{id}/deposits | Déposer un contenu chiffré pour le demandeur |
GET | /requests/{id}/deposits | Lister les dépôts (demandeur) ou vérifier que le lien est ouvert (sans jeton) |
DELETE | /requests/{id} | Fermer la demande et supprimer les dépôts |
POST | /abuse | Signaler un lien abusif |
POST | /webhooks | Enregistrer un webhook https |
DELETE | /webhooks/{id} | Supprimer un webhook |
POST | /domains | Réserver un domaine personnalisé |
GET | /domains/{domain} | État d'un domaine |
POST | /domains/{domain} | Vérifier l'enregistrement TXT et rattacher le domaine |
DELETE | /domains/{domain} | Libérer le domaine |
GET | /config | Limites de l'instance |
GET | /health | Santé du service |
Exemple (TypeScript, chiffrement local)
import { sendSecret, parseSecretLink, receiveSecret } from "@sesame-eclair/api-client";
const options = { baseUrl: "https://eclair.kayzen-lyon.com" };
// Encrypts in this process, then uploads only the ciphertext.
const sent = await sendSecret(options, {
content: { v: 1, type: "text", text: "DB_PASSWORD=..." },
views: 1,
ttlSeconds: 3600,
});
if (sent.ok) console.log(sent.url, sent.manageUrl);
// Recipient side: the key comes from the link fragment.
const link = parseSecretLink(sent.ok ? sent.url : "")!;
const received = await receiveSecret(options, link);Codes de réponse
404— même réponse pour un secret inconnu, expiré, déjà lu ou détruit.401— code PIN incorrect, avec attemptsLeft.403— access_denied : le réseau ou le pays du lecteur ne respecte pas les restrictions du secret. Le secret reste intact.423— pas encore lisible (révélation différée), avec availableAt.429— trop de requêtes, réessayez dans une heure.
Webhooks
Enregistrez une URL https avec POST /webhooks, puis passez webhookId à la création du secret. Vous recevez { event, secretId, at } pour created, revealed, burned, revoked et expired : jamais le contenu. Le signingSecret n'est montré qu'une fois ; vérifiez chaque appel ainsi :
import { createHmac, timingSafeEqual } from "node:crypto";
// header = request.headers["eclair-signature"], body = raw request body (string)
function verify(header: string, body: string, signingSecret: string): boolean {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window
const expected = createHmac("sha256", Buffer.from(signingSecret, "base64url"))
.update(`${t}.${body}`)
.digest();
return timingSafeEqual(expected, Buffer.from(v1, "hex"));
}Restrictions d'accès
accessRules: { cidrs: ["203.0.113.0/24"], countries: ["FR"] } limite l'ouverture à des réseaux ou des pays (20 entrées au plus chacun). La vérification a lieu avant tout envoi du chiffré ; un pays inconnu est refusé dès qu'une règle de pays existe.
Domaine personnalisé
POST /domains réserve un domaine et renvoie un enregistrement TXT de vérification et un CNAME. Une fois créés, POST /domains/{domain} vérifie le TXT et rattache le domaine ; le certificat TLS est émis automatiquement. La page Domaine personnalisé fait tout cela pour vous.