diff --git a/site/src/pages/warmup.astro b/site/src/pages/warmup.astro index f26128d8..e541e3dc 100644 --- a/site/src/pages/warmup.astro +++ b/site/src/pages/warmup.astro @@ -265,13 +265,82 @@ const faq = [ + +
+
+
+
Verification token
+

+ Every warmup message carries proof it is real. +

+

+ The single biggest threat to a shared warmup pool is a sender pretending to receive its own warmup. A signed HMAC token embedded in the body, validated on classification, makes that impossible. +

+
+ +
+ +
+
+
+ + + +
+ warmup token · embedded in body +
+
# Token format · plain text, base64url
+wbly:01HQX9F7P3A8KY2NJM4R6BWT0S:1746820392:3f9c2a8b5e1d740a
+   └ pool id            └ ulid msg id
+                                       └ expires            └ hmac-sha256
+
+# Embedded as a normal-looking footer line:
+Hey, just wanted to follow up on the project we discussed.
+Let me know when you have a chance.
+
+— Ben
+ref: wbly:01HQX9F7P3A8KY2NJM4R6BWT0S:1746820392:3f9c2a8b
+
+# Receiver classification → verify (HMAC + expiry + msg-id)
+if token.valid() && !seen(token.id) then credit_signal
+else increment_invalid_count(sender)
+
+ + +
+
Validation rules
+
+ {[ + { rule: 'HMAC signature', why: 'Tokens are signed with a per-pool key. Forged tokens fail verification.' }, + { rule: 'Expiry window', why: 'Tokens expire 48 hours after issue. Late replies do not count.' }, + { rule: 'Single-use ID', why: 'Each token ULID is recorded on first valid use. Replays count as invalid.' }, + { rule: 'Pool membership', why: 'Token pool ID must match the receiver\'s pool. Cross-pool tokens are rejected.' }, + { rule: 'Sender / recipient', why: 'Token sender and recipient mailbox IDs must match the message routing.' }, + ].map((r) => ( +
+
{r.rule}
+

{r.why}

+
+ ))} +
+
+ ≥ 3 invalid tokens / 24h → + mailbox quarantine +
+
+
+
+
+ -
+
-
What warmup does
+
The daily loop

One loop. One mailbox. One day.

@@ -286,17 +355,18 @@ const faq = [
    {[ - { n: '01', t: 'Pool member selection', b: 'A partner mailbox is drawn from the same pool the sender belongs to. Recent partners are excluded for 7 days to prevent degenerate pairs.' }, - { n: '02', t: 'Verification token', b: 'A signed token is embedded in the message body. Receivers extract and validate it on classification. Missing or replayed tokens count as suspicious.' }, - { n: '03', t: 'Send through worker', b: 'The mailbox\'s assigned worker dispatches the message during the recipient\'s business hours, with spacing that matches human typing cadence.' }, - { n: '04', t: 'Partner classification', b: 'The receiving mailbox classifies the message as inbox, Promotions, or spam and reports the result back along with the token.' }, - { n: '05', t: 'Score update', b: 'Placement, token validity, and any complaint signals feed the per-mailbox rolling spam score. Bands transition on the resulting score.' }, + { n: '01', t: 'Pool member selection', k: 'partner = pool.draw(exclude_recent: 7d)', b: 'A partner mailbox is drawn from the same pool the sender belongs to. Recent partners are excluded for 7 days to prevent degenerate pairs.' }, + { n: '02', t: 'Token issue + embed', k: 'token = hmac(pool_key, sender:recipient:ulid:exp)', b: 'A signed token is generated for this pair and inlined as a `ref:` footer in the body. Receivers extract and validate it on classification.' }, + { n: '03', t: 'Send through worker', k: 'worker.send(msg, schedule: business_hours(recipient_tz))', b: 'The mailbox\'s assigned worker dispatches the message during the recipient\'s business hours, with spacing that matches human typing cadence.' }, + { n: '04', t: 'Partner classification', k: 'receiver.classify(msg) → {inbox|promotions|spam}', b: 'The receiving mailbox classifies the message and reports the result back along with the token. Inbox arrivals are credited as positive placement signals.' }, + { n: '05', t: 'Score update + band', k: 'score = rolling_sum(placement, complaints, tokens)', b: 'The per-mailbox rolling score is updated. If thresholds are crossed, the mailbox transitions to Watch, Quarantined, or Blocked.' }, ].map((s) => (
  1. {s.n}
    {s.t}
    -

    {s.b}

    +
    {s.k}
    +

    {s.b}

  2. ))}