Build a Gmail One-Click Unsubscribe

Build a local RFC 8058 unsubscribe service with secure send suppression.

Introduction

30 Second Summary

An unsubscribe link should give you control over future emails. A careless design can remove someone from a mailing list simply because an automated system checked the link.

In this project, you will build a local Node.js service that handles an RFC 8058-style one-click unsubscribe POST for Gmail-compatible email. You will prove secure retries and send suppression before diagramming the production race boundary.

What You'll Build

When you demo the finished service, the dashboard flips to unsubscribed before the send path answers suppress, while tampered or repeated requests stay harmless.

By the end of this project, you'll have:

  • A local browser demo where you can compare an unsafe state-changing GET request with the secure one-click POST flow.
  • A repeatable simulator that makes tamper rejection, idempotency, and send-time suppression visible in one run.
  • A component diagram plus a sequence diagram you can use to explain the Final Suppression Gate at the unsubscribe/send race boundary.
  • Secret Mission: Extend the production diagrams with a retryable asynchronous audit pipeline and dead-letter path.

Are there any prerequisites?

This project assumes you are comfortable with distributed systems and basic JavaScript.

You need Visual Studio Code and Safari on a Mac. Step 1 confirms Node.js v24.21.0 before you begin.

Before We Start

This checkpoint commits you to building a sender-side unsubscribe system before any hands-on work begins. Gmail is the mail receiver, while your local service owns token validation, subscription state, and send-time suppression before future sends.

Prepare the Local Workspace

This protocol experiment needs one shared workspace for every local file. Visual Studio Code gives the project a clear home.

A pinned Node.js runtime removes version ambiguity before the experiment begins. This project uses v24.21.0.

In this step, get ready to:
  • Open gmail-unsubscribe-design as your Visual Studio Code workspace.
  • Verify the exact Node.js runtime.
  • Create the three empty JavaScript files for the prototype.
Open the project workspace

A workspace tells Visual Studio Code which folder belongs to this project. Its integrated terminal then starts from that folder.

  • Find Visual Studio Code with the macOS system search.
  • Select Visual Studio Code from the search results.

You will see Visual Studio Code open in its own window.

  • Select File from the top menu bar.
  • Select Open Folder... from the menu.

You will see the folder selection dialog.

  • Create a folder named gmail-unsubscribe-design in the folder selection dialog.

The folder selection dialog now shows gmail-unsubscribe-design as an available folder.

  • Select the gmail-unsubscribe-design folder.
  • Confirm the folder selection.

The Explorer sidebar now shows gmail-unsubscribe-design as the open workspace.

Verify the pinned Node.js runtime

Node.js runs both local unsubscribe servers. The simulator uses the same runtime.

  • Select View from the Visual Studio Code menu bar.
  • Select Terminal from the menu.

You will see the integrated terminal open at the bottom of the workspace.

Before you check, do you expect the installed runtime to match the project pin?

  • Check the installed Node.js version by running this command:
node --version

What does this check prove?

The command prints the Node.js runtime available to this terminal. The required output is v24.21.0.

✔️ I see the required version

Good work. The integrated terminal is using the exact Node.js runtime required by the prototype.

ⓧ I see a different version

Changing a local runtime can feel disruptive. The installer updates Node.js for new terminal sessions.

Your files inside gmail-unsubscribe-design stay unchanged.

Your browser downloads the pinned macOS installer.

  • Run node-v24.21.0.pkg from your browser downloads.

The macOS installer opens with the installation steps.

  • Approve the permission request if macOS presents one.
  • Complete the installer prompts.

You will see the installer report that the installation completed.

  • Switch back to the Visual Studio Code workspace from earlier.
  • Close the existing integrated terminal panel.

The earlier terminal session is now closed.

  • Select View from the menu bar.
  • Select Terminal from the menu.

The reopened terminal starts a fresh session that can detect the installed runtime.

Before you check again, do you expect the fresh terminal to report the pinned version?

  • Verify the updated Node.js version by running this command:
node --version

What should you see?

The terminal prints v24.21.0. The workspace now uses the pinned runtime.

Still seeing a different version?

Confirm that the downloaded package is named node-v24.21.0.pkg. Repeat the installation if macOS did not report completion.

Confirm that you closed the earlier terminal session after installation.

Ask for help with the remaining mismatch: Help me diagnose my Node.js version mismatch.

ⓧ Command not found

The terminal cannot locate Node.js. Installing the pinned macOS package provides the runtime required by this project.

Your browser downloads the pinned macOS installer.

  • Run node-v24.21.0.pkg from your browser downloads.

The macOS installer opens with the installation steps.

  • Approve the permission request if macOS presents one.
  • Complete the installer prompts.

You will see the installer report that the installation completed.

  • Switch back to the Visual Studio Code workspace from earlier.
  • Close the existing integrated terminal panel.

The terminal panel closes with the earlier command state.

  • Select View from the menu bar.
  • Select Terminal from the menu.

The new terminal session can now locate the installed Node.js runtime.

Before you check again, do you expect the command to return the pinned version?

  • Verify the installed Node.js version by running this command:
node --version

What should you see?

The terminal prints v24.21.0. Node.js is now available to the workspace.

Still unable to run Node.js?

Confirm that the macOS installer reported a completed installation. Confirm that you reopened the integrated terminal afterward.

Ask for help with the missing command: Help me diagnose the missing Node.js command.

With v24.21.0 visible in the terminal, the runtime ambiguity is gone.

Create the empty project files

Each JavaScript file has one role in the experiment. Empty files establish the project structure before the build steps add code.

  • Select gmail-unsubscribe-design in the Explorer sidebar.
  • Create an empty naive-server.js file with the new-file control.

The Explorer now lists naive-server.js inside the workspace.

  • Create an empty server.js file with the new-file control.

The Explorer now lists server.js below the first file.

  • Create an empty simulate-gmail.js file with the new-file control.

The Explorer now lists all three JavaScript files under gmail-unsubscribe-design.

  • Select naive-server.js in the Explorer.

The editor for naive-server.js contains no text.

  • Select server.js in the Explorer.

The editor for server.js contains no text.

  • Select simulate-gmail.js in the Explorer.

The editor for simulate-gmail.js contains no text.

Before the final check, do you expect the integrated terminal to report the exact pinned runtime?

  • Confirm the workspace runtime by running this command in the integrated terminal:
node --version

What confirms the workspace is ready?

The terminal prints v24.21.0. The Explorer lists the three empty JavaScript files required for the later build steps.

Does the final check look different?

Confirm that the integrated terminal belongs to the gmail-unsubscribe-design workspace. Check the three filenames for spelling differences.

Ask for help with the workspace check: Help me compare my prepared workspace with the required final state.

✔️ Awesome, I've got everything!

Everything lines up. Your workspace has the pinned runtime and the three empty files required for the protocol experiment.

ⓧ I'd like to double check the full code

The exact cumulative code at this point is three blank files.

The naive-server.js file contains no text.

The server.js file contains no text.

The simulate-gmail.js file contains no text.

Your pinned local workspace is ready. Next, you will run the deliberately unsafe GET design and observe its failure mode.

Experience the Unsafe GET Design

Your local workspace is ready after verifying Node.js v24.21.0. This experiment starts with the simplest unsubscribe flow.

Links can be fetched automatically before a reader acts. This experiment tests what happens when an HTTP GET request owns the state change.

In this step, get ready to:
  • Build the intentionally unsafe local server.
  • Inspect the initial subscribed dashboard in Safari.
  • Prove that one GET request mutates the subscription.
Build the naive server

The demonstration stores one subscription in memory. Its status value supplies the state shown on the dashboard.

  • In the Visual Studio Code Explorer, select naive-server.js.
  • Add the HTTP import plus the local subscription data by pasting this code:
const http = require('node:http');

const PORT = 3000;
const ORIGIN = `http://localhost:${PORT}`;

const subscription = {
  id: 'sub-001',
  email: 'learner@example.com',
  listId: 'system-design-weekly',
  status: 'subscribed',
};

What does this foundation define?

  • The node:http module provides the local web server.
  • The PORT plus ORIGIN values keep the service at http://localhost:3000.
  • The subscription object begins with the subscribed state that you will test.
  • Save naive-server.js.
  • Check that the foundation parses by running this command:
node naive-server.js

What should you see?

The integrated terminal returns to its prompt without a syntax error. This clean return confirms that Node.js parsed the import plus subscription data.

Seeing a syntax error?

Check that require('node:http') uses matching parentheses plus quotes. Confirm that the subscription object closes with };.

Help me check the foundation in my naive unsubscribe server.

The page renderer reads the current subscription each time it builds a response. This makes every state change visible in the browser.

  • Add the dashboard renderer below the subscription object by pasting this code:
function renderPage(message = 'The subscription is ready for the experiment.') {
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Naive Unsubscribe Demo</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 760px; margin: 48px auto; padding: 0 20px; }
    code, pre { background: #f4f4f4; padding: 4px 6px; border-radius: 4px; }
    .status { font-size: 1.25rem; }
    .warning { color: #9a3412; }
  </style>
</head>
<body>
  <h1>Naive GET unsubscribe</h1>
  <p class="status">Current state: <strong>${subscription.status}</strong></p>
  <p>${message}</p>
  <p><a href="/naive-unsubscribe?subscriptionId=${subscription.id}">Unsubscribe with GET</a></p>
  <p class="warning">This link mutates state as soon as it is fetched. A link scanner could trigger it.</p>
  <p><a href="/">Refresh dashboard</a></p>
</body>
</html>`;
}

How does the dashboard expose state?

  • The page inserts subscription.status into the current-state display.
  • The unsubscribe link includes the record identifier as subscriptionId.
  • The warning describes why automatic link fetching becomes dangerous when the link mutates state.
  • Save naive-server.js.
  • Check that the dashboard template parses by running this command:
node naive-server.js

What should you see now?

The terminal returns to its prompt again. This clean return confirms that the HTML template is valid JavaScript.

Seeing a template error?

Check that the HTML template begins after the return keyword. Confirm that it closes before the final function brace.

Help me repair the dashboard template in naive-server.js.

The request handler connects the dashboard to a state-changing route. A matching subscription identifier lets that route update the in-memory record.

  • Add the request handler plus server listener below renderPage() by pasting this code:
const server = http.createServer((request, response) => {
  const url = new URL(request.url, ORIGIN);
  let message = 'The subscription is ready for the experiment.';

  if (request.method === 'GET' && url.pathname === '/naive-unsubscribe') {
    const subscriptionId = url.searchParams.get('subscriptionId');

    if (subscriptionId === subscription.id) {
      subscription.status = 'unsubscribed';
      message = 'A GET request changed subscription state without a confirmation POST.';
    }
  }

  const body = renderPage(message);
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  response.end(body);
});

server.listen(PORT, () => {
  console.log(`Naive demo running at ${ORIGIN}`);
});

Where does the state change happen?

  • The route condition requires request.method === 'GET'.
  • The same condition requires the '/naive-unsubscribe' path.
  • A matching identifier reaches subscription.status = 'unsubscribed'; and mutates the record.
  • The response renders the updated object into the dashboard.
  • Locate request.method === 'GET' in naive-server.js.
  • Locate url.pathname === '/naive-unsubscribe' in the same condition.
  • Follow the matching block to subscription.status = 'unsubscribed';.
  • Save naive-server.js.
  • Start the naive server by running this command. Expect the process to keep this terminal occupied until you stop it later:
node naive-server.js

What should the terminal show?

The terminal prints a line containing http://localhost:3000. The running process is now ready to respond to browser requests.

Did the server exit?

Confirm that server.listen(PORT, () => { appears at the end of naive-server.js. Check the terminal for a line number that points to a missing brace or parenthesis.

Help me diagnose why my naive Node.js server does not stay running.

✔️ Awesome, I've got everything!

Your complete naive server is saved. Keep the process running for the browser experiment.

ⓧ I'd like to double check the full code

const http = require('node:http');

const PORT = 3000;
const ORIGIN = `http://localhost:${PORT}`;

const subscription = {
  id: 'sub-001',
  email: 'learner@example.com',
  listId: 'system-design-weekly',
  status: 'subscribed',
};

function renderPage(message = 'The subscription is ready for the experiment.') {
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Naive Unsubscribe Demo</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 760px; margin: 48px auto; padding: 0 20px; }
    code, pre { background: #f4f4f4; padding: 4px 6px; border-radius: 4px; }
    .status { font-size: 1.25rem; }
    .warning { color: #9a3412; }
  </style>
</head>
<body>
  <h1>Naive GET unsubscribe</h1>
  <p class="status">Current state: <strong>${subscription.status}</strong></p>
  <p>${message}</p>
  <p><a href="/naive-unsubscribe?subscriptionId=${subscription.id}">Unsubscribe with GET</a></p>
  <p class="warning">This link mutates state as soon as it is fetched. A link scanner could trigger it.</p>
  <p><a href="/">Refresh dashboard</a></p>
</body>
</html>`;
}

const server = http.createServer((request, response) => {
  const url = new URL(request.url, ORIGIN);
  let message = 'The subscription is ready for the experiment.';

  if (request.method === 'GET' && url.pathname === '/naive-unsubscribe') {
    const subscriptionId = url.searchParams.get('subscriptionId');

    if (subscriptionId === subscription.id) {
      subscription.status = 'unsubscribed';
      message = 'A GET request changed subscription state without a confirmation POST.';
    }
  }

  const body = renderPage(message);
  response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  response.end(body);
});

server.listen(PORT, () => {
  console.log(`Naive demo running at ${ORIGIN}`);
});
Inspect the starter dashboard

The first browser view establishes the baseline for the experiment. The page should reflect the record's original subscribed value.

  • Press Cmd+Space to open the macOS search bar.
  • Type Safari in the search field.
  • Press Return to open Safari.
  • Select the Smart Search field.
  • Enter http://localhost:3000.
  • Press Return to load the dashboard.

You will see the Naive GET unsubscribe dashboard. Its current state reads subscribed.

Dashboard not loading?

  • Return to the integrated terminal in Visual Studio Code.
  • Confirm that the server process is still running.
  • Check that Safari contains the complete http://localhost:3000 address.

Help me connect Safari to my local naive unsubscribe server.

Trigger the unsafe GET

The dashboard link points directly to the mutation route. Following it sends the subscription identifier through the URL.

Before you select the link, predict which subscription state Safari will show after the request.

  • In Safari, select Unsubscribe with GET.

Safari now shows the current state as unsubscribed. The address contains /naive-unsubscribe?subscriptionId=sub-001.

Why is this unsafe?

The server changes subscription state as soon as the GET reaches the route. An automated link fetcher can therefore cause the same mutation without a deliberate unsubscribe action.

This visible state change is the intended failure in the experiment. The route gives a read-style request authority to modify the record.

  • Switch back to Visual Studio Code.
  • Press Control-C in the integrated terminal to stop the naive server.

The terminal prompt returns after the process stops. Port 3000 is now free for the next service.

You exposed the exact failure: one navigation changed subscription state. Next, you will separate the manual browser flow from the authenticated one-click request.

Build the RFC 8058 Endpoint

The unsafe server proved that an HTTP GET request can change subscription state without the recipient choosing it. The secure service needs a boundary that keeps link viewing safe.

In this step, you will build an RFC 8058-style service with Node.js. An HMAC-signed token binds each unsubscribe request to a subscription identifier.

Manual GET requests show a confirmation form. Authenticated POST requests validate the one-click body before updating the subscription.

In this step, get ready to:
  • Build the signed-token unsubscribe service.
  • Trace the manual GET and automated POST paths.
  • Verify the RFC 8058 header preview in Safari.
Build the signed-token service

The secure service uses built-in cryptography to turn a subscription identifier into a signed capability token. The server can reject altered tokens before they reach the subscription update.

  • In the Explorer sidebar in Visual Studio Code, select the empty server.js file.
  • Use the full-code reference below to replace the empty file.

✔️ Awesome, I've got everything!

  • Confirm that server.js contains the complete secure service.

ⓧ I'd like to double check the full code

  • Replace the contents of server.js with this complete file:
const http = require('node:http');
const { createHmac, timingSafeEqual } = require('node:crypto');
const { Buffer } = require('node:buffer');

const PORT = 3000;
const LOCAL_ORIGIN = `http://localhost:${PORT}`;
const EXAMPLE_ORIGIN = 'https://example.com';
const LOCAL_DEMO_SECRET = 'local-demo-secret-change-me';

const subscription = {
  id: 'sub-001',
  email: 'learner@example.com',
  listId: 'system-design-weekly',
  status: 'subscribed',
  version: 1,
  unsubscribedAt: null,
};

const auditEvents = [];

function resetSubscription() {
  subscription.status = 'subscribed';
  subscription.version = 1;
  subscription.unsubscribedAt = null;
  auditEvents.length = 0;
}

function signSubscriptionId(subscriptionId) {
  return createHmac('sha256', LOCAL_DEMO_SECRET)
    .update(subscriptionId)
    .digest('hex');
}

function createToken(subscriptionId) {
  return `${subscriptionId}.${signSubscriptionId(subscriptionId)}`;
}

function verifyToken(token) {
  const parts = token.split('.');

  if (parts.length !== 2) {
    return null;
  }

  const [subscriptionId, suppliedSignature] = parts;

  if (!subscriptionId || !/^[a-f0-9]{64}$/.test(suppliedSignature)) {
    return null;
  }

  const expectedSignature = signSubscriptionId(subscriptionId);
  const suppliedBuffer = Buffer.from(suppliedSignature, 'hex');
  const expectedBuffer = Buffer.from(expectedSignature, 'hex');

  if (suppliedBuffer.length !== expectedBuffer.length) {
    return null;
  }

  return timingSafeEqual(suppliedBuffer, expectedBuffer)
    ? subscriptionId
    : null;
}

function unsubscribe(subscriptionId) {
  if (subscriptionId !== subscription.id) {
    return null;
  }

  const changed = subscription.status !== 'unsubscribed';

  if (changed) {
    subscription.status = 'unsubscribed';
    subscription.version += 1;
    subscription.unsubscribedAt = new Date().toISOString();
    auditEvents.push({
      id: `unsubscribe-${subscription.version}`,
      type: 'subscription.unsubscribed',
      subscriptionId: subscription.id,
      occurredAt: subscription.unsubscribedAt,
    });
  }

  return {
    status: subscription.status,
    changed,
    version: subscription.version,
  };
}

function sendJson(response, statusCode, payload) {
  const body = JSON.stringify(payload, null, 2);
  response.writeHead(statusCode, {
    'Content-Type': 'application/json; charset=utf-8',
    'Content-Length': Buffer.byteLength(body),
  });
  response.end(body);
}

function sendHtml(response, statusCode, body) {
  response.writeHead(statusCode, {
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Length': Buffer.byteLength(body),
  });
  response.end(body);
}

function readBody(request) {
  return new Promise((resolve, reject) => {
    let body = '';
    request.setEncoding('utf8');
    request.on('data', (chunk) => {
      body += chunk;
    });
    request.on('end', () => resolve(body));
    request.on('error', reject);
  });
}

function headerExamples() {
  const token = createToken(subscription.id);
  const publicUrl = `${EXAMPLE_ORIGIN}/unsubscribe/${token}`;

  return {
    headers: {
      'List-Unsubscribe': `<${publicUrl}>`,
      'List-Unsubscribe-Post': 'List-Unsubscribe=One-Click',
    },
    visibleBodyLink: publicUrl,
    note: 'example.com is documentation-only. A production URL must be your reachable HTTPS endpoint.',
  };
}

function renderDashboard() {
  const token = createToken(subscription.id);
  const localManualUrl = `${LOCAL_ORIGIN}/unsubscribe/${token}`;
  const publicUrl = `${EXAMPLE_ORIGIN}/unsubscribe/${token}`;

  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>One-Click Unsubscribe Service</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 900px; margin: 48px auto; padding: 0 20px; }
    code, pre { background: #f4f4f4; padding: 8px; border-radius: 6px; overflow-wrap: anywhere; }
    .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
    .card { border: 1px solid #d4d4d4; border-radius: 8px; padding: 16px; }
    @media (max-width: 700px) { .grid { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <h1>Gmail-compatible unsubscribe prototype</h1>
  <p>This is a local protocol simulation. It does not send email or make Gmail display its unsubscribe control.</p>
  <div class="grid">
    <section class="card">
      <h2>Subscription state</h2>
      <pre>${JSON.stringify(subscription, null, 2)}</pre>
      <form method="post" action="/reset">
        <button type="submit">Reset subscription</button>
      </form>
    </section>
    <section class="card">
      <h2>Actions</h2>
      <ul>
        <li><a href="/status">Inspect JSON status</a></li>
        <li><a href="/headers">Inspect email header examples</a></li>
        <li><a href="/send-decision">Ask the send pipeline</a></li>
        <li><a href="${localManualUrl}">Open the manual GET flow</a></li>
      </ul>
    </section>
  </div>
  <h2>Example outgoing message values</h2>
  <pre>List-Unsubscribe: &lt;${publicUrl}&gt;
List-Unsubscribe-Post: List-Unsubscribe=One-Click

Visible body link: ${publicUrl}</pre>
  <h2>Audit events</h2>
  <pre>${JSON.stringify(auditEvents, null, 2)}</pre>
</body>
</html>`;
}

function renderManualPage(token) {
  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Confirm Unsubscribe</title>
</head>
<body style="font-family: system-ui, sans-serif; max-width: 680px; margin: 48px auto; padding: 0 20px;">
  <h1>Confirm unsubscribe</h1>
  <p>A manual GET displays this page without changing subscription state.</p>
  <form method="post" action="/unsubscribe/${token}">
    <input type="hidden" name="List-Unsubscribe" value="One-Click">
    <button type="submit">Unsubscribe</button>
  </form>
  <p><a href="/">Return to dashboard</a></p>
</body>
</html>`;
}

const server = http.createServer(async (request, response) => {
  try {
    const url = new URL(request.url, LOCAL_ORIGIN);

    if (request.method === 'GET' && url.pathname === '/') {
      sendHtml(response, 200, renderDashboard());
      return;
    }

    if (request.method === 'POST' && url.pathname === '/reset') {
      resetSubscription();
      sendJson(response, 200, { reset: true, subscription });
      return;
    }

    if (request.method === 'GET' && url.pathname === '/status') {
      sendJson(response, 200, { subscription, auditEvents });
      return;
    }

    if (request.method === 'GET' && url.pathname === '/headers') {
      sendJson(response, 200, headerExamples());
      return;
    }

    if (request.method === 'GET' && url.pathname === '/demo-token') {
      sendJson(response, 200, { token: createToken(subscription.id) });
      return;
    }

    if (request.method === 'GET' && url.pathname === '/send-decision') {
      sendJson(response, 200, {
        subscriptionId: subscription.id,
        decision: subscription.status === 'subscribed' ? 'send' : 'suppress',
        version: subscription.version,
      });
      return;
    }

    const unsubscribePrefix = '/unsubscribe/';

    if (url.pathname.startsWith(unsubscribePrefix)) {
      const token = decodeURIComponent(url.pathname.slice(unsubscribePrefix.length));
      const subscriptionId = verifyToken(token);

      if (subscriptionId !== subscription.id) {
        sendJson(response, 401, { error: 'Invalid unsubscribe token' });
        return;
      }

      if (request.method === 'GET') {
        sendHtml(response, 200, renderManualPage(token));
        return;
      }

      if (request.method === 'POST') {
        const contentType = (request.headers['content-type'] || '').split(';')[0];

        if (contentType !== 'application/x-www-form-urlencoded') {
          sendJson(response, 415, { error: 'Expected application/x-www-form-urlencoded' });
          return;
        }

        const body = await readBody(request);
        const form = new URLSearchParams(body);

        if (form.get('List-Unsubscribe') !== 'One-Click') {
          sendJson(response, 400, { error: 'Expected List-Unsubscribe=One-Click' });
          return;
        }

        const result = unsubscribe(subscriptionId);
        sendJson(response, 200, result);
        return;
      }
    }

    sendJson(response, 404, { error: 'Not found' });
  } catch (error) {
    sendJson(response, 500, { error: error.message });
  }
});

server.listen(PORT, () => {
  console.log(`Secure unsubscribe service running at ${LOCAL_ORIGIN}`);
  console.log(`Header preview: ${LOCAL_ORIGIN}/headers`);
});

How is the service organized?

  • The import group loads the HTTP server module.
  • The cryptography functions create and verify the signed capability token.
  • The response helpers return JSON or HTML with explicit content lengths.
  • The route handler separates dashboards, header previews, send decisions, and unsubscribe requests.
  • Save server.js.
  • Locate the imports for node:http, node:crypto, and node:buffer.
  • Trace signSubscriptionId() into createToken().
  • Read verifyToken() from the token split through the signature comparison.

How does token verification work?

  • The signSubscriptionId() function signs the subscription identifier with createHmac().
  • The createToken() function joins the identifier to its hexadecimal signature.
  • The verifyToken() function rejects malformed tokens before comparing signatures.
  • The timingSafeEqual() function compares equal-length byte buffers without exposing comparison timing.

The hard-coded LOCAL_DEMO_SECRET keeps this local prototype self-contained. A production service stores signing material in protected configuration with a rotation plan.

Trace the two unsubscribe paths

The manual flow and the automated flow share one signed URL. Their request methods give each visit a different responsibility.

The GET path renders a confirmation form without calling unsubscribe(). The POST path validates every required input before changing state.

  • In server.js, scroll to const unsubscribePrefix = '/unsubscribe/';.
  • Follow the token extraction into verifyToken().
  • Read the GET branch that calls renderManualPage().
  • Continue into the POST branch that checks the request content type.
  • Locate the exact List-Unsubscribe=One-Click value check.
  • Trace the validated subscription identifier into unsubscribe().

How do the paths divide responsibility?

The manual GET path displays a page with an explicit confirmation button. Viewing the signed URL leaves the subscription state unchanged.

The automated POST path requires application/x-www-form-urlencoded. It also requires the exact List-Unsubscribe=One-Click form value.

The capability token supplies the authorization for this local flow. The request does not depend on cookies or HTTP authorization state.

The unsubscribe() function provides idempotency by adding an audit event only when the state changes.

Run and inspect the secure flow

The integrated terminal from the unsafe experiment is available again because that server has stopped. The secure service can now claim the same local port.

  • Switch back to the integrated terminal from earlier.
  • Start the secure unsubscribe service by running this command:
node server.js

What should you see?

The terminal prints Secure unsubscribe service running at http://localhost:3000. It also prints Header preview: http://localhost:3000/headers.

The command keeps running because the service is listening for requests. Leave this terminal active for the browser checks.

Service did not start?

Confirm that the earlier naive server is no longer running. A process that still owns port 3000 prevents the secure service from listening there.

Check that the integrated terminal is inside gmail-unsubscribe-design. Node.js resolves server.js from the terminal's current folder.

Help me diagnose why the secure unsubscribe service does not start.

  • Switch back to Safari.
  • Select the Smart Search field.
  • Enter http://localhost:3000.
  • Press Return.

You will see the Gmail-compatible unsubscribe prototype. The subscription card shows subscribed with version 1.

  • Select Open the manual GET flow in the Actions card.

You will see a confirmation page with an Unsubscribe button. Reaching this page through GET has not changed the record.

  • Select Return to dashboard.

The dashboard still shows subscribed with version 1. This proves that opening the manual link is safe.

Before the final check, predict which two header fields the service should return.

  • Select Inspect email header examples in the Actions card.

You will see List-Unsubscribe with an angle-bracketed HTTPS URL under example.com. The List-Unsubscribe-Post field contains List-Unsubscribe=One-Click.

Header preview missing?

Confirm that the browser address ends with /headers on port 3000. Keep the terminal running while Safari loads the response.

Check that server.js contains the headerExamples() function and the GET route for /headers.

Help me diagnose why the RFC 8058 header preview is missing.

That is the secure request boundary in place. Next, you will send tampered and duplicate requests to prove that the service rejects forgery while preserving one state transition.

Prove Idempotency and Send Suppression

The secure Node.js endpoint now validates signed unsubscribe requests. Next, you will test hostile input plus repeated delivery.

A valid endpoint still needs idempotency because receivers can retry a POST. The sending path must also consult the updated subscription before provider handoff.

In this step, get ready to:
  • Build a repeatable client for the local unsubscribe service.
  • Test a tampered token plus two deliveries of the valid RFC 8058 request.
  • Confirm that one state transition produces one audit event plus a suppressed send decision.
Build the HTTP request helper

The simulator needs a reusable client for every request in the experiment. The secure service in the first integrated terminal must remain running while this client uses the second terminal.

  • Create a second integrated terminal using the plus button in the Visual Studio Code Terminal panel.
  • Select the empty simulate-gmail.js file in the Explorer sidebar.
  • Load the built-in HTTP client plus byte-length helper by pasting these imports at the top of simulate-gmail.js:
const http = require('node:http');
const { Buffer } = require('node:buffer');

Why These Imports?

  • The http module lets the simulator send requests to the local service.
  • The Buffer helper calculates the request body's encoded byte length.
  • Add the reusable request helper below the imports by pasting this code:
function requestText({ path, method = 'GET', headers = {} }, body = '') {
  return new Promise((resolve, reject) => {
    const request = http.request({
      hostname: 'localhost',
      port: 3000,
      path,
      method,
      headers,
    }, (response) => {
      let responseBody = '';
      response.setEncoding('utf8');
      response.on('data', (chunk) => {
        responseBody += chunk;
      });
      response.on('end', () => {
        resolve({ statusCode: response.statusCode, body: responseBody });
      });
    });

    request.on('error', reject);

    if (body) {
      request.write(body);
    }

    request.end();
  });
}

What Does This Helper Do?

  • The options select the local service on port 3000 plus the requested path.
  • The response listeners collect the entire response body before resolving the promise.
  • The error listener rejects the promise when the simulator cannot reach the service.
  • The body check writes form data only when the request includes a payload.
  • Save simulate-gmail.js.
  • Check the current file for JavaScript syntax errors in the second terminal by running:
node --check simulate-gmail.js

What Should You See?

The terminal returns to the prompt without reporting a syntax error. This confirms that the request helper parses successfully.

Seeing a Syntax Error?

Compare the closing braces plus parentheses in requestText with the code above. Confirm that both imports remain at the top of simulate-gmail.js.

Help me fix the syntax in my simulator request helper.

Add the unsubscribe scenarios

Each simulated one-click request must send the exact form value expected by the secure endpoint. The same helper delivers both the tampered token plus the repeated valid token.

  • Add the one-click POST helper below requestText by pasting this code:
function postUnsubscribe(token) {
  const body = 'List-Unsubscribe=One-Click';

  return requestText({
    path: `/unsubscribe/${encodeURIComponent(token)}`,
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length': Buffer.byteLength(body),
    },
  }, body);
}

What Does This Request Carry?

  • The request path safely inserts the signed token into the unsubscribe URL.
  • The POST body carries the exact List-Unsubscribe=One-Click value required by the local endpoint.
  • The content type tells the server to parse the body as URL-encoded form data.
  • The content length describes the encoded body size in bytes.
  • Save simulate-gmail.js.
  • Check the expanded file for syntax errors by running:
node --check simulate-gmail.js

What Does This Check Prove?

The terminal returns to the prompt without a syntax error. The request helper plus one-click POST helper now form valid JavaScript.

Does the Syntax Check Fail?

Confirm that postUnsubscribe sits after the closing brace of requestText. Check that every header name retains its quotes.

Help me find the syntax problem in my one-click POST helper.

The main simulation resets the in-memory record before every run. It then turns a valid HMAC token into a tampered token before testing the valid token twice.

  • Add the simulation function plus its invocation below postUnsubscribe by pasting this code:
async function runSimulation() {
  await requestText({ path: '/reset', method: 'POST' });

  const tokenResponse = await requestText({ path: '/demo-token' });
  const { token } = JSON.parse(tokenResponse.body);
  const finalCharacter = token.at(-1);
  const tamperedToken = `${token.slice(0, -1)}${finalCharacter === 'a' ? 'b' : 'a'}`;

  const tampered = await postUnsubscribe(tamperedToken);
  console.log('Tampered request:', tampered.statusCode, tampered.body);

  const first = await postUnsubscribe(token);
  console.log('First valid request:', first.statusCode, first.body);

  const duplicate = await postUnsubscribe(token);
  console.log('Duplicate request:', duplicate.statusCode, duplicate.body);

  const decision = await requestText({ path: '/send-decision' });
  console.log('Send decision:', decision.statusCode, decision.body);
}

runSimulation().catch((error) => {
  console.error('Simulation failed:', error.message);
});

How Does the Experiment Work?

  • The reset request gives every run the same subscribed starting state.
  • The token request retrieves a valid signed capability from the local service.
  • The tampering logic changes the final signature character without changing the subscription identifier.
  • The two valid POSTs reveal whether a retry produces another state transition.
  • The final request asks the sending path to decide whether delivery remains allowed.
  • Save the completed simulate-gmail.js file.

✔️ Awesome, I've got everything!

  • Confirm that simulate-gmail.js is saved in the gmail-unsubscribe-design folder.

ⓧ I'd like to double check the full code

const http = require('node:http');
const { Buffer } = require('node:buffer');

function requestText({ path, method = 'GET', headers = {} }, body = '') {
  return new Promise((resolve, reject) => {
    const request = http.request({
      hostname: 'localhost',
      port: 3000,
      path,
      method,
      headers,
    }, (response) => {
      let responseBody = '';
      response.setEncoding('utf8');
      response.on('data', (chunk) => {
        responseBody += chunk;
      });
      response.on('end', () => {
        resolve({ statusCode: response.statusCode, body: responseBody });
      });
    });

    request.on('error', reject);

    if (body) {
      request.write(body);
    }

    request.end();
  });
}

function postUnsubscribe(token) {
  const body = 'List-Unsubscribe=One-Click';

  return requestText({
    path: `/unsubscribe/${encodeURIComponent(token)}`,
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Content-Length': Buffer.byteLength(body),
    },
  }, body);
}

async function runSimulation() {
  await requestText({ path: '/reset', method: 'POST' });

  const tokenResponse = await requestText({ path: '/demo-token' });
  const { token } = JSON.parse(tokenResponse.body);
  const finalCharacter = token.at(-1);
  const tamperedToken = `${token.slice(0, -1)}${finalCharacter === 'a' ? 'b' : 'a'}`;

  const tampered = await postUnsubscribe(tamperedToken);
  console.log('Tampered request:', tampered.statusCode, tampered.body);

  const first = await postUnsubscribe(token);
  console.log('First valid request:', first.statusCode, first.body);

  const duplicate = await postUnsubscribe(token);
  console.log('Duplicate request:', duplicate.statusCode, duplicate.body);

  const decision = await requestText({ path: '/send-decision' });
  console.log('Send decision:', decision.statusCode, decision.body);
}

runSimulation().catch((error) => {
  console.error('Simulation failed:', error.message);
});

How to Use This Reference

This reference shows the exact completed contents of simulate-gmail.js. Its order preserves the request helper before the functions that call it.

Run the simulator and inspect the audit state

Before you run the simulator, which requests do you expect to change the subscription record?

  • Run the repeatable experiment from the second integrated terminal with this command:
node simulate-gmail.js

What Should the Simulation Prove?

  • The Tampered request: result shows status 401 because the changed signature fails validation.
  • The First valid request: result shows "changed": true because the subscription moves to unsubscribed.
  • The Duplicate request: result shows "changed": false because the state already matches the requested outcome.
  • The Send decision: result shows "decision": "suppress" because the sending path reads the updated state.

That is the core proof. Retries preserve one state transition while the send path blocks future delivery.

Did the Simulation Fail to Connect?

Confirm that the first integrated terminal still shows the secure service running on port 3000. Confirm that you ran the simulator from the gmail-unsubscribe-design folder.

Help me diagnose why the local simulator cannot complete its unsubscribe requests.

Before you inspect the service state, how many unsubscribe audit events should two valid POST deliveries produce?

  • Switch back to Safari from earlier.
  • Select the Smart Search field.
  • Enter http://localhost:3000/status.
  • Press Return.

You will see an unsubscribed record at version 2 with a populated unsubscribedAt value. The auditEvents list contains exactly one subscription.unsubscribed event.

Why Is There Only One Event?

The first valid POST changes the state plus records the transition. The duplicate POST returns the existing result without creating another event.

This makes retries safe while preserving an accurate audit trail.

Your local service now proves tamper rejection, idempotent state updates, plus send-time suppression. Next, you will map that behavior onto the production race boundary.

Diagram the Production System

Your simulator has proven tamper rejection, idempotency, and send suppression against the local service.

You will now turn that behavior into a production design using draw.io in Safari.

The local code proves the endpoint behavior. The diagrams expose the exact race boundary where an unsubscribe can still stop a prepared email before provider handoff becomes irreversible.

In this step, get ready to:
  • Map the receiver path and sending path on a component page.
  • Model the protocol alternatives and unsubscribe race on a sequence page.
  • Publish the completed design as editable and image artifacts.
Build the component view

A component diagram shows which part of the production system owns each decision. The receiver lane begins with Gmail or another mail user agent.

The sending lane prepares an email before consulting the subscription state. The authoritative store connects both lanes through the final suppression read.

  • Return to Safari from earlier.
  • Open the draw.io online editor.
  • Select Device if draw.io asks where to store your diagram.
  • Select File from the top menu.
  • Select New.
  • Choose Blank to start with an empty canvas.

Why use two pages?

The component page shows ownership and dependencies. The sequence page shows how timing changes the final outcome.

Keeping both views in one diagram file makes the system easier to explain without crowding either canvas.

  • Right-click the first page tab at the bottom of the canvas.
  • Select Rename.
  • Enter Component in the page name field.
  • Select Rename to apply the page name.

Place the consistency boundary

Keeping eight components readable is a little fiddly. Use one horizontal lane for incoming unsubscribe requests and another horizontal lane for outgoing messages.

The HMAC validator protects the receiver lane. The Final Suppression Gate protects the sending lane.

  • Add a top-left box labelled Gmail or mail user agent from the left shape panel.
  • Add a box labelled unsubscribe endpoint to its right.
  • Add a box labelled HMAC token validator to the right of the endpoint.
  • Add a box labelled authoritative subscription store to the right of the validator.
  • Add a lower-left box labelled campaign scheduler.
  • Add a box labelled render or personalization stage to the right of the scheduler.
  • Add a box labelled Final Suppression Gate to the right of message preparation.
  • Add a box labelled provider or MTA handoff immediately after the gate.

Read the two paths

  • The upper path receives an unsubscribe request before validating its signed capability token.
  • The lower path prepares a message before making its final suppression decision.
  • The authoritative store supplies the state used by that final decision.
  • Connect Gmail or mail user agent to unsubscribe endpoint with an arrow.
  • Connect unsubscribe endpoint to HMAC token validator with an arrow.
  • Connect HMAC token validator to authoritative subscription store with an arrow.
  • Connect campaign scheduler to render or personalization stage with an arrow.
  • Connect render or personalization stage to Final Suppression Gate with an arrow.
  • Connect Final Suppression Gate to provider or MTA handoff with an arrow.
  • Connect Final Suppression Gate to authoritative subscription store with an arrow labelled strongly consistent read.

Your component page now places the Final Suppression Gate after message preparation and immediately before provider handoff. That position minimizes the window where an unsubscribe can race with a prepared message.

Map the production sequence

A sequence diagram orders requests and state changes over time. This view makes the boundary between suppressible work and irreversible handoff visible.

  • Select the plus symbol in the page tab bar.
  • Right-click the new page tab.
  • Select Rename.
  • Enter Sequence in the page name field.
  • Select Rename to apply the page name.

What does the sequence page add?

The component page shows where each responsibility lives. The sequence page proves which event became visible first.

That ordering determines whether the prepared message is dropped or handed to the provider.

  • Add a lifeline labelled mail user agent on the left.
  • Add a lifeline labelled unsubscribe endpoint to its right.
  • Add a lifeline labelled token validator next.
  • Add a lifeline labelled authoritative subscription store next.
  • Add a lifeline labelled send worker next.
  • Add a lifeline labelled Final Suppression Gate next.
  • Add a lifeline labelled provider or MTA handoff on the right.

Read time from top to bottom

Each message lower on the canvas happens later. Alternative frames let one diagram preserve several possible outcomes.

  • Add an alternative frame labelled invalid token.
  • Draw a tampered POST from mail user agent to unsubscribe endpoint.
  • Draw a validation request from unsubscribe endpoint to token validator.
  • Label the rejected response 401.
  • Add an alternative frame labelled duplicate POST.
  • Draw the repeated valid POST to unsubscribe endpoint.
  • Draw the idempotency check against authoritative subscription store.
  • Label the duplicate response changed: false.

Why preserve these alternatives?

The invalid-token path rejects unauthorized state changes. The duplicate path returns success without recording a second transition.

Together they carry the simulator's security and idempotency evidence into the production design.

The first race branch uses strong consistency at the final gate. A committed unsubscribe becomes visible before the sender reaches the provider.

  • Add a race frame labelled unsubscribe committed before a strongly consistent final authoritative read.
  • Draw a self-message on send worker labelled prepare message.
  • Draw a message from unsubscribe endpoint to authoritative subscription store labelled commit unsubscribe.
  • Draw a message from Final Suppression Gate to authoritative subscription store labelled strongly consistent read.
  • Label the gate outcome suppress.

First race outcome

The authoritative read sees the committed unsubscribe. The sender drops the prepared message before the provider receives it.

This is the side of the boundary where the sender still controls delivery.

The second branch crosses the boundary first. The provider accepts the message before the unsubscribe commit becomes visible to the sender.

  • Add a race frame labelled provider handoff before unsubscribe commit.
  • Draw a message from Final Suppression Gate to authoritative subscription store labelled read subscribed.
  • Draw a message from Final Suppression Gate to provider or MTA handoff labelled successful provider handoff.
  • Draw a later message from unsubscribe endpoint to authoritative subscription store labelled commit unsubscribe.
  • Add a note labelled the already handed-off message may still arrive.

Second race outcome

The committed unsubscribe blocks later messages. The sender cannot guarantee recall after successful provider handoff.

This is the irreversible side of the race boundary.

An eventually consistent cache can reduce unnecessary work early in the send path. The final gate needs current authoritative state.

  • Add the note a commit visible before the final authoritative read is suppressible.
  • Add the note a message successfully handed to the provider is beyond the sender service's recall guarantee.
  • Add the note an eventually consistent cache or replica may be used for an early filter but not for the final gate.
  • Add the note an unavailable authoritative store should delay or fail closed for marketing mail.

Your sequence page now preserves both race outcomes. It also states the availability policy that protects recipients when the authoritative decision cannot be read.

Export the final design

The .drawio file preserves editable shapes and both pages. The PNG export provides a shareable view with embedded diagram data.

  • Select File from the top menu.
  • Select Save As.
  • Enter production-unsubscribe.drawio as the diagram filename.
  • Select Device as the storage location.
  • Select OK to save the editable diagram locally.

The editor title now shows production-unsubscribe.drawio. Your editable production design is safely stored on the Mac.

  • Select the Sequence page tab.
  • Select File from the top menu.
  • Select Export As.
  • Select PNG.
  • Keep Include a copy of my diagram selected.
  • Select Export.
  • Enter production-unsubscribe.png as the export filename.
  • Save the PNG to your Mac.

Before you reopen the artifacts, which race branch should end at suppress?

  • Select File from the draw.io menu.
  • Select Open.
  • Choose production-unsubscribe.drawio from its saved location.
  • Select the Component page tab.
  • Select the Sequence page tab.

What should you see?

The Component page shows the receiver path and sending path. The Final Suppression Gate sits immediately before provider or MTA handoff.

The Sequence page shows invalid-token and duplicate-POST alternatives. It also shows suppression before handoff and the loss of recall guarantees after handoff.

  • Select File from the draw.io menu.
  • Select Open.
  • Choose production-unsubscribe.png from its saved location.

The PNG opens with the sequence design and embedded editable diagram data. You can still inspect the two race branches because Include a copy of my diagram was enabled.

Missing a page or diagram data?

  • Reopen production-unsubscribe.drawio if either page tab is missing from the PNG view.
  • Export the Sequence page again with Include a copy of my diagram enabled if the PNG opens as a flat image.
  • Confirm both filenames use the exact .drawio and .png extensions.

Help me check my draw.io component and sequence pages.

You have turned the working prototype into a production argument. The diagrams now show exactly when the sender can still stop a message.

✔️ Awesome, I've got everything!

Your editable diagram and PNG are saved. Both artifacts explain the suppression boundary and its guarantees.

ⓧ I'd like to double check the final state

  • Confirm production-unsubscribe.drawio contains Component and Sequence pages.
  • Confirm the component page includes the mail user agent, endpoint, validator, authoritative store, scheduler, rendering stage, final gate, and provider handoff.
  • Confirm the Final Suppression Gate appears after message preparation and immediately before provider handoff.
  • Confirm the sequence page includes the invalid-token and duplicate-POST alternatives.
  • Confirm the first race branch suppresses a message after the unsubscribe commits.
  • Confirm the second race branch completes provider handoff before the unsubscribe commits.
  • Confirm the notes cover authoritative reads, eventual consistency, recall limits, and fail-closed behavior.
  • Confirm production-unsubscribe.png opens with the embedded diagram data.

Secret mission

Add an Asynchronous Audit Pipeline

Extend the production diagrams with an at-least-once Audit Queue. Add a retrying Audit Consumer with event-based idempotency. Route exhausted retries to a Dead-Letter Queue without delaying HTTP success or suppression.

Clean Up Your Resources

Clean Up Your Resources

Choose whether to keep the files, pause the local service, or delete the prototype. There are no ongoing costs.

Resources you used:

  • A local Node.js service using port 3000.
  • The in-memory subscription record inside server.js.
  • The in-memory audit list inside server.js.
  • The gmail-unsubscribe-design workspace containing naive-server.js, server.js, and simulate-gmail.js.
  • An editable draw.io file named production-unsubscribe.drawio.
  • A PNG export named production-unsubscribe.png.

Keep everything running

Keep every project artifact if you plan to refine the prototype. The local service can stop because its durable work lives in the workspace files.

  • Return to the Visual Studio Code integrated terminal that is running server.js.
  • Press Control-C to stop the local service.
  • Retain the gmail-unsubscribe-design workspace on your Mac.
  • Retain production-unsubscribe.drawio for future diagram changes.
  • Retain production-unsubscribe.png for sharing the production design.

Stopping the service clears the in-memory subscription record and audit list. Your code and diagrams stay saved.

Pause - I'll come back to this later

Pause the project when you want a clean terminal without removing your work. The saved files let you resume from the secure service.

  • Return to the integrated terminal that is running server.js.
  • Press Control-C to stop the local service.
  • Close Visual Studio Code after your file changes are saved.
  • Restart the service later from the gmail-unsubscribe-design workspace by running this command:
node server.js

What Does This Command Do?

The node command runs server.js from the workspace. The service recreates a fresh in-memory subscription record and audit list on port 3000.

Service Does Not Restart?

  • Confirm the integrated terminal is inside gmail-unsubscribe-design.
  • Stop any earlier process that is still using port 3000.
  • Help me restart the local server.js service.

Delete - I don't want to use this again

Deleting the folder removes the complete prototype from your Mac. This action does not affect Gmail or any cloud service.

  • Return to the integrated terminal that is running server.js.
  • Press Control-C to stop the local service.
  • Use Finder to move the gmail-unsubscribe-design folder to Trash.
  • Move production-unsubscribe.drawio to Trash if it is stored outside gmail-unsubscribe-design.
  • Move production-unsubscribe.png to Trash if it is stored outside gmail-unsubscribe-design.
  • Remove these items permanently from Trash when you are certain you no longer need them.
  • Refresh http://localhost:3000 in Safari to verify the local dashboard no longer loads.

The failed connection confirms the local service is no longer running. Its in-memory subscription record and audit list are gone.

Nice Work!

Nice Work!

You did it! You've built a local Gmail-compatible one-click unsubscribe service that demonstrates secure state changes and production race boundaries.

What you learned:

  • Exposed the unsafe GET failure mode in a local Node.js service. Replaced it with a signed RFC 8058-style POST flow. Validated List-Unsubscribe=One-Click before changing subscription state. Protected each subscription identifier with an HMAC signature plus timingSafeEqual.
  • Used the repeatable simulator to reject a tampered token. Proved idempotency when a duplicate POST left state unchanged. Confirmed send-time suppression when the send path returned suppress for the unsubscribed record.
  • Mapped the production components around a Final Suppression Gate. Placed its authoritative read immediately before provider handoff. Documented both sides of the unsubscribe/send race boundary. A commit visible before the final read suppresses the message. A completed provider handoff remains beyond the sender's recall guarantee.
  • Secret Mission: Extended production-unsubscribe.drawio with an at-least-once Audit Queue. Added a retrying Audit Consumer with an event-derived idempotency key. Routed exhausted retries to a Dead-Letter Queue. Kept HTTP success plus final suppression independent from audit delivery.

Ready to quiz yourself?