■ space · notes de terrain · fig 1

■ la réponse d'abord

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.

1,258lignes — toutes lisibles
2tables — abonnés + envois
1l'en-tête fait la désinscription

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 serveurPlateforme hébergée
Lignes de code que je peux lire1 258, les toutes0
Propriété de la listeun fichier SQLitebouton d'export
Désinscriptionen-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 envoiun week-endune soirée
la plateforme gagne—la réputation de délivrabilité, et le ne-pas-avoir-à-penser
le fichier gagne—une liste auditable dans un éditeur, à un rsync de la disparition
l'exigence—en février 2024, Google et Yahoo ont fait de la désinscription en un clic une règle pour expéditeurs de masse — RFC 8058

02LE CHEMIN D'ABONNEMENT

un POST, trois gardes — la signature est la seule autorité, si bien que la porte s'ouvre le registre endormi

FIG 1 · LA PORTE DU DORMEURtouches 1 · 2 · 3 — ou les boutons
LA PORTE DU DORMEURHMAC-SHA256 · RFC 8058 · PAS DE SESSIONREGISTRE · ENDORMIABONNÉS · ENVOISOFFPAS DE FILFRAPPERCONFIRMERSCELLÉ∞ SANS-EXP

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

l'endpoint — un seul POST
curl -X POST https://mrsaynothing.dev/letters/api/subscribe \
  -H 'content-type: application/json' \
  -d '{"email":"[email protected]"}'
db.ts—deux tables : subscribers, un CHECK épinglant status à pending · confirmed · unsubscribed, et sends, la piste d'audit
le honeypot—un champ de formulaire nommé website humans never see — a filled field buys a fake ok que les humains ne voient jamais — un champ rempli achète un faux
the bucket, rien d'autrele seau
enumeration10 requêtes par heure par IP · une réémission par adresse toutes les 10 minutesl'énumération
the status lockdes réponses délibérément identiques que l'adresse soit déjà abonnée ou nonle verrou de statutSET status='maybe' is refused

une contrainte CHECK a un seul métier — THE TOKEN PRESS

LA PRESSE À JETONS

votre moitié calcule partout — la moitié de la boîte est murée ; ce mur est la sécurité
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}`;
}
votre moitié — calculable icipayload = base64url(email|action) → cmVhZGVyQGV4YW1wbGUuY29tfGNvbmZpcm0
la moitié de la boîte — muréeMAC = HMAC-SHA256(payload, secret) · 43 caractères — calculé sur la boîte de service
le token sur le lientoken = cmVhZGVyQGV4YW1wbGUuY29tfGNvbmZpcm0.mac
vérification—timingSafeEqual contre un MAC recalculé — pas de table de jetons, pas d'expiration à gérer
la clé—vit hors du dépôt, montée en lecture seule à l'exécution — la presse calcule votre moitié et refuse de fabriquer l'autre
tokens de désabonnement—n'expirent jamais, exprès — la mécanique du consentement doit continuer de fonctionner pour toujours ; révoquer signifie faire tourner la clé
la bizarrerie Gmail—la désinscription en un clic tire un POST cross-origin — cette route fait confiance au MAC ; la vérification d'origine se retire

Un 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

ERR_PNPM_IGNORED_BUILDSpnpm 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.
Le CSRF a bloqué GmailLe POST en un clic venu des serveurs de Gmail n'a aucune preuve same-origin, évidemment. La route fait désormais confiance au token — et le token est inforgeable.
le plafond de délivrabilitéSans DKIM aligné, mail-tester note le setup autour de 5/10, et les récepteurs stricts peuvent jeter le courrier aux indésirables. C'est le coût honnête du refus du relais payant — la parade est connue et patiente, si la liste le justifie un jour.
tracking—pas de tracking à l'ouverture — un principe : les pixels de tracking sont la raison pour laquelle les gens se méfient des newsletters
périmètre—une liste, pas de segments, pas d'ordonnancement au-delà d'une CLI appelée à la main ou par un timer
sauvegardes—copier le fichier, restaurer le fichier — le mode WAL garde la copie cohérente pendant que le serveur tourne
le reste—le surveiller au démarrage sous supervision systemd est la charge ops restante
l'envoi — une commande
# the --dry flag renders the full text and HTML for review before anything moves
cli.mjs send --subject ... --file post.md
■ cachet d'honnêteté Testé sur : node:sqlite (WAL) · tokens HMAC-SHA256 · en-têtes RFC 8058 · SMTP simple. Première publication 2026-09-26. Le compte de lignes, les limites de débit et la note mail-tester sont les propres chiffres du serveur ; la RFC est citée depuis le dossier IETF et l'exigence Gmail/Yahoo depuis leurs consignes d'expéditeurs. Note de la migration : la plateforme sur laquelle cette pièce tournait a depuis été retirée — la pièce demeure le dossier de ce qui fut bâti et de ce qu'elle a enseigné.

fin de la newsletter à fichier unique · rien de fetché · tout est local