martbost 0374b04f0d Default-deny outbound mail and a sign-up gate
This is a test area on a testnet contract, but it was seeded on 15 Sep with a copy of
InstantAdPay's live member list and it carried a working SendGrid key. On 18 Sep the
coach's stall nudges fired on schedule and emailed 95 real people from it. One of them
clicked through and opened a fresh account two hours later.

Nothing was misconfigured. Every send site checked mailer.hasKey(), the key was there,
so every send site got a yes. The default was wrong, not the plumbing.

OUTBOUND=on is now required before anything can leave: hasKey() is false without it,
send() rejects outright, and the two mailing loops (coach nudges, lead drip) never start.
SIGNUPS=closed shuts the three registration doors and reports signupsOpen:false, which
also drops the email-code card from the UI. Members already here keep their dashboards.

A missing env var means silence now, not delivery.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 04:29:08 -05:00

LinkSpin — site

Membership advertising with immutable on-chain settlement. Zero-dependency Node server (RM Circle pattern): static pages + JSON API + SSE ledger.

Architecture

  • server.js — http server: pages, /api/*, /join/<id> sponsor links, SSE feed
  • chain.js — contract reader + persistent event indexer (free public RPCs)
  • auth.js — SIWE wallet sign-in (EIP-4361), sessions in the volume
  • accounts.js — site-side records only (free members, last-touch sponsor attribution, handles). The CHAIN is the source of truth for money/credits.
  • public/ — landing, live ledger, member area; no client libraries

Chain flip (rehearsal → mainnet)

Everything chain-specific lives in data/config.json (volume): {contract, chainId, chainName, explorer, rpcs, deployBlock}. Defaults point at the Amoy rehearsal deployment. Launch = deploy the mainnet contract, wipe accounts.json/sessions.json/chain-index.json, PATCH /api/admin/chain with the mainnet values, set rehearsal:false via /api/admin/site. Same code, different config.

Run

PORT=3100 node server.js       # DATA_DIR defaults to ./data

Admin API auth: Authorization: Bearer $ADMIN_PASSWORD.

Spec: ../CONTRACT-SPEC.md (v1.0.3-frozen). Contracts: ../contracts/.

S
Description
LinkSpin: the network rotator (test area)
Readme 8.1 MiB
Languages
JavaScript 76%
HTML 19%
CSS 4.8%
Shell 0.2%