La newsletter sur un nœud : fichier sqlite — 1 258 lignes, aucune plateforme
Double opt-in, désinscription en un clic et une CLI d'envoi sur 1 258 lignes auditables — l'en-tête RFC 8058 qui l'a rendue courte, et le plafond de délivrabilité honnête.
■ space · notes de terrain · fig 1
Toute la newsletter tient en 1 258 lignes : double opt-in, désinscription en un clic, une CLI d'envoi — pas de compte plateforme.
La désinscription en un clic est un seul en-tête — RFC 8058, exigé par Google et Yahoo depuis février 2024.
Plafond honnête : sans DKIM aligné, mail-tester ~5/10 et les récepteurs stricts peuvent jeter aux indésirables — bien pour une petite liste ; un relais répare.
Une newsletter, c'est trois endpoints, deux tables, et un en-tête d'email que la plupart des plateformes vous cachent.
01POURQUOI S'AUTO-HÉBERGER DU TOUT ?
l'inventaire honnête — l'échange en un écran ; le fichier gagne la propriété, la plateforme gagne le ne-pas-avoir-à-penser
| Ce serveur | Plateforme hébergée | |
|---|---|---|
| Lignes de code que je peux lire | 1 258, les toutes | 0 |
| Propriété de la liste | un fichier SQLite | bouton d'export |
| Désinscription | en-tête RFC 8058, auto-signé | réglage de tableau de bord |
| Plafond de délivrabilité | ~5/10 mail-tester tant que DKIM n'est pas aligné | leur réputation |
| Temps jusqu'au premier envoi | un week-end | une soirée |
rsync de la disparition02LE CHEMIN D'ABONNEMENT
un POST, trois gardes — la signature est la seule autorité, si bien que la porte s'ouvre le registre endormi
ROUTE 2 · CONFIRMER — LA JAUGE LIT LE SCELLÉ · REGISTRE ENDORMI · VERDICT CONFIRME ENCORE
le tag porte base64url(email|action) plus un scellé HMAC-SHA256 — pas de session, pas de table de jetons, rien à consulter
curl -X POST https://mrsaynothing.dev/letters/api/subscribe \
-H 'content-type: application/json' \
-d '{"email":"[email protected]"}'
subscribers, un CHECK épinglant status à pending · confirmed · unsubscribed, et sends, la piste d'auditwebsite humans never see — a filled field buys a fake ok que les humains ne voient jamais — un champ rempli achète un faux SET status='maybe' is refusedune contrainte CHECK a un seul métier — THE TOKEN PRESS
LA PRESSE À JETONS
import { createHmac, timingSafeEqual } from 'node:crypto';
// payload = base64url(email|action), MAC = HMAC-SHA256(payload)
export function sign(email: string, action: 'confirm' | 'unsub'): string {
const payload = Buffer.from(`${email}|${action}`).toString('base64url');
const mac = createHmac('sha256', config.hmacSecret).update(payload).digest('base64url');
return `${payload}.${mac}`;
}
timingSafeEqual contre un MAC recalculé — pas de table de jetons, pas d'expiration à gérerUn lien de confirmation qui fonctionne encore quand la base de données dort, c'est une chose de moins qui peut pourrir.
04CE QUI A CASSÉ, CE QU'IL NE FAIT PAS
trois pannes, réparées — et le plafond, imprimé par le setup lui-même
pnpm install --frozen-lockfile tournait propre en local et mourait dans le conteneur — le script postinstall d'esbuild n'a jamais été approuvé. La parade : embarquer le petit pnpm-workspace.yaml declaring allowed build scripts into the image. Half a day to find, one file to fix.# the --dry flag renders the full text and HTML for review before anything moves
cli.mjs send --subject ... --file post.md
fin de la newsletter à fichier unique · rien de fetché · tout est local