■ space · полевые записи · fig 1

■ сначала ответ

Вся рассылка — это 1 258 строк: двойной opt-in, отписка в один клик, CLI отправки — без аккаунта платформы.

Отписка в один клик — это один заголовок — RFC 8058, обязательный для Google и Yahoo с февраля 2024 года.

Честный потолок: без выровненного DKIM, mail-tester ~5/10 и строгие получатели могут закинуть в спам — для небольшого списка нормально; релей это чинит.

1,258строк — все читаемы
2таблицы — подписчики + отправки
1заголовок делает отписку

Рассылка — это три эндпоинта, две таблицы и один email-заголовок, который большинство платформ от вас прячет.

01ВООБЩЕ ЗАЧЕМ САМОХОСТИНГ?

честная инвентаризация — весь размен на одном экране; файл выигрывает владение, платформа — не-надо-думать

Этот серверХостинговая платформа
Строк кода, которые я могу читатьвсе 1 2580
Владение спискомодин файл SQLiteкнопка экспорта
Отписказаголовок RFC 8058, самоподписанныйнастройка в дашборде
Потолок доставляемости~5/10 mail-tester, пока DKIM не выровнених репутация
Время до первой отправкивыходныевечер
платформа выигрывает—репутация доставляемости и не-надо-думать
файл выигрывает—аудируемый список в редакторе, в одном rsync от исчезновения
требование—в феврале 2024 Google и Yahoo сделали отписку в один клик правилом для массовых отправителей — RFC 8058

02ПУТЬ ПОДПИСКИ

один POST, три охранника — подпись — единственная власть, так что дверь открывается при спящем реестре

РИС 1 · ДВЕРЬ СПЯЩЕГОклавиши 1 · 2 · 3 — или кнопки
ДВЕРЬ СПЯЩЕГОHMAC-SHA256 · RFC 8058 · БЕЗ СЕССИИРЕЕСТР · СПИТПОДПИСЧИКИ · ОТПРАВКИВЫКЛБЕЗ ПРОВОДАЧЕКАНИТЬПОДТВЕРДИТЬСКРЕПЛЕНО∞ БЕЗ-СРОКА

МАРШРУТ 2 · ПОДТВЕРДИТЬ — ПРИБОР ЧИТАЕТ СКРЕПУ · РЕЕСТР СПИТ · ВЕРДИКТ ВСЁ ЕЩЁ ПОДТВЕРЖДАЕТ

тег несёт base64url(email|action) плюс скрепу HMAC-SHA256 — ни сессии, ни таблицы токенов, нечего и вычислять

эндпоинт — один POST
curl -X POST https://mrsaynothing.dev/letters/api/subscribe \
  -H 'content-type: application/json' \
  -d '{"email":"[email protected]"}'
db.ts—две таблицы: subscribers, CHECK прибивает status к pending · confirmed · unsubscribed, и sends, аудит-след
хонейпот—скрытое поле формы с именем website humans never see — a filled field buys a fake ok, которого люди никогда не видят — заполненное поле покупает фальшивое
the bucket, ничего большеведро
enumeration10 запросов в час с одного IP · одно переотправление на адрес каждые 10 минутперебор адресов
the status lockответы намеренно одинаковые, подписан адрес уже или нетзамок статусаSET status='maybe' is refused

CHECK-ограничение делает одну работу — THE TOKEN PRESS

ПРЕСС ДЛЯ ТОКЕНОВ

ваша половина вычисляется где угодно — половина бокса замурована; этот мур и есть безопасность
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}`;
}
ваша половина — вычислима здесьpayload = base64url(email|action) → cmVhZGVyQGV4YW1wbGUuY29tfGNvbmZpcm0
половина бокса — замурованаMAC = HMAC-SHA256(payload, secret) · 43 символа — считает бокс обслуживания
токен на ссылкеtoken = cmVhZGVyQGV4YW1wbGUuY29tfGNvbmZpcm0.mac
проверка—timingSafeEqual против пересчитанного MAC — ни таблицы токенов, ни срока годности
ключ—живёт вне репозитория, смонтирован read-only на рантайме — пресс считает вашу половину и отказывается подделывать чужую
токены отписки—не истекают никогда, нарочно — механика согласия должна работать вечно; отозвать значит ротировать ключ
причуда Gmail—отписка в один клик шлёт кросс-доменный POST — тот маршрут верит MAC; проверка происхождения отходит в сторону

Линк подтверждения, который работает, пока база спит, — одна вещь меньше, способная сгнить.

04ЧТО СЛОМАЛОСЬ, ЧЕГО ОН НЕ УМЕЕТ

три поломки, починены — и потолок, напечатанный самим setup-ом

ERR_PNPM_IGNORED_BUILDSpnpm install --frozen-lockfile работал чисто локально и умирал в контейнере — postinstall-скрипт esbuild так и не был одобрен. Фикс: вложить в образ маленький pnpm-workspace.yaml со списком разрешённых build-скриптов. Полдня на поиск, один файл на починку.
CSRF заблокировал GmailОднокликовый POST с серверов Gmail не несёт полномочий same-origin, очевидно. Маршрут теперь верит токену — а токен невозможно подделать.
потолок доставляемостиБез выровненного DKIM mail-tester ставит setup-у около 5/10, а строгие получатели могут закинуть письмо в спам. Это честная цена отказа от платного релея — фикс известен и дожидается, когда список его оправдает.
tracking—никакого open-tracking — принцип: трекинг-пиксели — причина, по которой люди не доверяют рассылкам
размах—один список, никаких сегментов, никакого планирования сверх CLI, зываемой рукой или таймером
бэкапы—скопировать файл, восстановить файл — режим WAL держит копию консистентной, пока сервер жив
остальное—следить, чтобы он стартовал под systemd-подобным присмотром, — вот и оставшаяся ops-забота
отправка — одна команда
# the --dry flag renders the full text and HTML for review before anything moves
cli.mjs send --subject ... --file post.md
■ честный штамп Проверено на: node:sqlite (WAL) · токены HMAC-SHA256 · заголовки RFC 8058 · простой SMTP. Первая публикация 2026-09-26. Число строк, лимиты запросов и балл mail-tester — собственные цифры сервера; RFC цитируется по летописи IETF, требование Gmail/Yahoo — по их правилам для отправителей. Заметка из миграции: платформа, на которой жила эта вещь, с тех пор уволена на покой — вещь остаётся летописью того, что было выстроено, и чему оно научило.

конец рассылки в одном файле · ничего не fetched · всё локально