Skip to content

Releases: MattGeiger/LOTTO

LOTTO v2.0.0-rc.8 — Cloudflare public reads and pill navigation

Choose a tag to compare

@MattGeiger MattGeiger released this 09 Oct 01:08

Highlights

  • Public branding, visitor language catalogs, completed translation packs and active announcements use an agency-scoped Cloudflare publication when enabled, including cold renders and compatible older anonymous requests. Missing or invalid publications never fall back to Neon.
  • Successful staff configuration writes refresh the public copy. Publication failures preserve committed changes and use bounded recovery; successful publication creates no recurring job.
  • Language catalog reads no longer seed database rows. Explicit staff initialization uses one idempotent bulk insert.
  • Public queue loading and recovery use Cloudflare snapshots, retaining the 120-second WebSocket reconnect grace and compatible heartbeats. Authenticated staff reads remain authoritative in Neon.
  • The selected core navigation tab is now a pill within the desktop navigation pill, surrounding its icon and label while preserving spacing, tap areas and Arcade's separate styling.

Validation

RC.8 is deployed to William Temple House and St. Johns Food Share using their separate infrastructure and configuration documents. Controlled intervals of 80 anonymous requests per agency added no covered branding, language, translation or queue SELECTs; staff positive controls increased database counters. These bounded results are not a measured monthly cost saving. A 48–72-hour compute-rate comparison remains in progress.

The final housekeeping check passed 90 tests in nine files. Earlier release verification includes production builds, lint, Worker checks, a 44-chunk legacy scan and a user-assisted iOS 15.4 simulator check for RC.7. Fresh email sign-in and physical iPad testing were not repeated for RC.8. RC.7/RC.8 introduced no dependency graph changes.

Deployment requirements

Configuration publication is opt-in. Deploy the additive Worker routes, publish and independently validate the agency's document for this exact application version, then enable LOTTO_PUBLIC_CONFIGURATION_SNAPSHOT_ENABLED=true. Every version change requires explicit activation of its matching publication. Preserve each agency's existing credentials, namespace, state and origins.

See publication operations, the Neon read audit and the changelog for the implementation and verification limits.

This is a release candidate, published as the newest LOTTO prerelease.

LOTTO v2.0.0-rc.4 — AI Configuration refresh

Choose a tag to compare

@MattGeiger MattGeiger released this 15 Sep 05:49

AI Configuration refresh

Ports FEED 1.8.0's current OpenAI, Anthropic, and Google catalog into LOTTO's existing six-step wizard. Twelve current presets, model-specific request parameters, and lifecycle/replacement guidance are available; existing saved models, keys, and custom reference prices remain intact.

LOTTO retains its simplified experience. Model Pricing shows reference input/output rates, with provider portals remaining the place to check spending. No built-in cost tracking or budget dashboard was added. Test key and model checks the selected model without billed generation and clearly describes that limit.

Validation

  • 963 tests passed (135 files), 1 skipped; TypeScript, lint, production build, and the 48-chunk legacy scan passed.
  • Authenticated WTH production creation/edit/deletion, blank-key preservation, zero-valued top-p, custom prices, selected-model success/failure, and legacy recovery passed. The disposable inactive record was removed; original configurations and public state were preserved.
  • Live structured generation passed on 11 of 12 models. Representative 100-row batches and single-text checks passed on all three providers. Conservative test-charge bound: $1.53315, below the approved $5 budget; provider portals determine actual charges.
  • iPad mini 4 Simulator on the actual iOS 15.4 runtime passed built/hosted login interactivity checks. Desktop responsive wizard checks and hosted hydration smoke passed.

Known validation limits

Claude Opus 5 returned explicit refusals with the test key, including a minimal food fixture; successful generation with that model remains unverified. Full authenticated legacy touch/wizard testing and physical iPad acceptance remain unverified. These limits are recorded rather than inferred from modern-browser tests.

Deployment and evidence

This prerelease is deployed directly to the WTH Vercel project at williamtemple.app. St. Johns remains on its existing deployment. The shared main branch was not advanced; reconcile the release branch before any future main-based WTH deployment.

The attached verification JSON records the exact tagged commit, final Vercel deployment, rollback target, and post-deployment checks. No schema, environment, or dependency changes are required.

Stable-v2 realtime acceptance remains separate.

LOTTO v1.26.0 — Appearance parity and cross-device polish

Choose a tag to compare

@MattGeiger MattGeiger released this 31 Aug 05:29

LOTTO v1.26.0 promotes the complete FEED-aligned Appearance workflow to the stable release line.

Highlights:

  • Tailwind v4 color-story workflow with FEED-aligned image handling, logo extraction, family/weight picking, four-mode previews, and legacy-safe runtime theme emission.
  • A live Appearance preview card beside Admin configuration management.
  • Consistent blurred backdrops for all blocking modal and confirmation overlays.
  • Cross-device fixes for WTH card gradients, dark-mode shadow hue, iOS 15 navigation highlighting, custom Next Up gradients, Arcade languages/branding/safe areas, Hi-viz typography, and installed-app pull-to-refresh.
  • Refreshed Help and GitHub project screenshots plus a current README.
  • A new v1.30 Arcade candidate plan covering traditional/public-domain mechanics, original-expression guardrails, accessibility, localization, and iOS 15 gates.

Verification: 821 tests pass across 121 files (one expected production-only fixture skipped); lint, production build, 42-chunk legacy scan, and production hydration/interactivity smoke all pass.

See CHANGELOG.md and docs/RELEASES.md for the full implementation record.

LOTTO v1.25.1 — SMTP refused in production

Choose a tag to compare

@MattGeiger MattGeiger released this 26 Aug 23:47

LOTTO v1.25.1 makes the SMTP transport structurally unavailable in production.

The gap

Every branch in auth-email-service.ts that reached smtpTransport() already checked isProduction — except the one where RESEND_API_KEY is absent or malformed, which fell through unguarded.

That was reachable in principle. src/lib/auth.ts refuses to start in production without a valid key, but /api/auth/otp/request imports the email service directly and never loads that config, so the OTP path was not covered by it. Delivery in production depended on the environment being correct rather than on the code refusing to do otherwise.

Caller Route Previously guarded?
sendMagicLinkEmail via src/lib/auth.ts ✅ config throws without a valid key
sendOtpEmail /api/auth/otp/request ❌ imports the service directly

Why it matters more than its severity

Practical impact of the old behaviour was low: an SMTP attempt on Vercel would have dialled localhost:1025, been refused, and failed the request closed without sending mail. The nodemailer advisory also needs attacker-controlled message options, and LOTTO builds the message itself.

The value is in what it settles. That advisory requires nodemailer 9.0.1 or later, which falls outside the Auth.js peer range of ^7.0.7 || ^8.0.5, so it cannot be resolved by upgrading while Auth.js stays on its current line. Production can no longer construct an SMTP transport at all — which makes the advisory's dev-only reachability a property of the code rather than of the deployment configuration.

Tests

tests/auth-email-transport.test.ts covers the delivery-transport contract, which previously had no tests at all. Both exported senders are exercised, because they are reached by different routes with different guards in front of them:

  • production refuses SMTP on the OTP path, the magic-link path, and when the key is present but malformed
  • Resend still delivers when the key is valid
  • development keeps the local SMTP/MailDev path on both senders

The three guard assertions were confirmed to fail with the guard removed, so they are regression guards rather than descriptions of current behaviour.

110 test files, 795 tests, tsc, lint, a production build and the legacy-bundle scan all pass.

Full changelog: v1.25.0...v1.25.1

LOTTO v1.25.0 — Next.js 16.3.2, advisories cleared

Choose a tag to compare

@MattGeiger MattGeiger released this 26 Aug 23:09

LOTTO v1.25.0 upgrades Next.js from 16.0.10 to 16.3.2 and closes the remaining dependency advisories. The client bundle drops roughly 176 KB — 48 chunks / 3,394,148 bytes down to 41 chunks / 3,218,514 bytes.

Dependencies

  • next 16.0.10 → 16.3.2.
  • sharp → ^0.35.4. Next 16.3 bundles its own sharp 0.35.4, which reintroduced the libvips duplicate-class collision from v1.24.1 — but mirrored: the root copy sat on 0.34.5 (libvips 8.17.3) while Next resolved 8.18.6, loading two dylibs into one process. In v1.24.1 the fix was to revert; here it is to move forward.
  • nodemailer → ^8.0.11. Blocked in v1.24.1 when @auth/core required ^6.8.0; the beta.32 upgrade in v1.24.3 widened both Auth.js peer ranges to ^7.0.7 || ^8.0.5, opening the 8.x line. Resolves six of seven nodemailer advisories including the CVSS 7.5 addressparser DoS.
  • linkify-it needed no upgrade. The lockfile had pinned a stale markdown-it 14.2.0, holding linkify-it at 5.0.1; ordinary resolution moves to 14.3.0 and 5.0.2, which is patched. An override forcing markdown-it 15 was tried and reverted once the simpler resolution proved sufficient — tiptap-markdown declares ^14.1.0, so that pairing is untested upstream.

What remains

A single unfixed issue: nodemailer's message-level raw option bypassing disableFileAccess/disableUrlAccess, which needs 9.0.1+ and falls outside the Auth.js peer range. npm audit reports it as four entries because nodemailer 8 now satisfies that peer range and npm can traverse the edge to @auth/core, @auth/pg-adapter and next-auth — at 7.0.10 the mismatch hid it. Entry count went 2 → 4 while real vulnerabilities went 8 → 1. The count is the misleading figure. Not visitor-reachable: production requires Resend and never constructs an SMTP transport.

Fixed

  • 18 pre-existing TypeScript errors across 13 test files. Not introduced by the upgrade — the same 18 are present on 16.0.10, confirmed by running tsc against both dependency sets and diffing normalized error lists. Next 16.0.10's build silently skipped typechecking tests; 16.3.2 honours tsconfig's include. One was a real defect: appearance-logo-upload.test.tsx omitted a required templates prop. npx tsc --noEmit is clean for the first time.
  • Rendered Markdown links now look like links — blue and underlined, across Announcements, Help and Release Notes. Colour comes from a new brand-independent --link token: a link is a universal affordance, and a green link inside Lift Up's green body copy would carry no signal.
  • npm run dev works again on the iPadOS 15 floor. Next 16.3 ships a React dev build that calls eval(), and Safari 15 does not treat ws: as covered by connect-src 'self'. Both relaxations are development-only; production headers verified byte-identical.

Verification

789 tests across 109 files, tsc, lint, a production build and the legacy-bundle scan all pass. Because a framework upgrade rebuilds every client chunk, a static scan is not sufficient evidence for the support floor — verification was done on a simulated iPad mini 4 running iPadOS 15.4 with a custom appearance applied, in both dev and production builds, confirming hydration rather than merely that the page painted.

Full changelog: v1.24.3...v1.25.0

LOTTO v1.24.3 — Auth.js upgrade, zero remaining criticals

Choose a tag to compare

@MattGeiger MattGeiger released this 26 Aug 15:30

LOTTO v1.24.3 completes the security work begun in v1.24.1. Upgrading next-auth to 5.0.0-beta.32 and @auth/pg-adapter to 1.11.3 brings @auth/core to 0.41.3 and clears the last three critical advisories.

Production advisories: 6, none critical — against 16 (3 critical) before v1.24.1.

Upstream now fixes what v1.24.1 patched by hand

@auth/core 0.41.3 makes a non-OK session response yield no session rather than an error object, so existence checks fail closed (GHSA-8fpg-xm3f-6cx3), and applies NFKC email normalization (GHSA-7rqj-j65f-68wh). Those are precisely the two weaknesses v1.24.1 had to mitigate in LOTTO's own code.

Both LOTTO mitigations are retained, not reverted. Each is stricter than its upstream counterpart:

LOTTO mitigation Upstream fix Why it stays
proxy.ts requires a populated session.user !!auth now fails closed Stricter — an error object with no user is still refused
ASCII screen on the raw address NFKC normalization Runs first, and rejects confusables that normalize into ASCII

On the beta channel

The v5 line has been in beta for roughly 1,000 days across 33 releases, with no committed stable date and a cadence that has been lengthening rather than converging. That is not an argument for staying on beta.30 — v4 does not support the App Router, so the real choice was between an older beta carrying three criticals and a current one that fixes them.

next-auth remains pinned exactly, without a caret, enforced by tests/security-nextauth-pin.test.ts.

Also fixed

The nodemailer peer-dependency drift reported during v1.24.1: @auth/core required ^6.8.0 against an installed 7.0.10, producing an ERESOLVE warning on every Vercel build. Both packages now declare ^7.0.7 || ^8.0.5.

Verification

No client code changed. 47 of 48 built chunks are byte-identical to v1.24.2 and total chunk bytes are unchanged; the only difference is inlined package.json metadata carrying the two new version strings. next-auth's client surface (SessionProvider, signIn) is untouched — consistent with beta.31 changing no next-auth source and every @auth/core fix being server-side.

beta.31's stricter email validation was exercised against the live OTP route rather than assumed: well-formed and plus-addressed staff addresses pass validation and the allowlist, unauthorized domains are refused, malformed forms (quoted local parts, doubled @, empty domain) are rejected, and a U+3000 ideographic space is still caught by LOTTO's own ASCII screen.

780 tests, lint, tsc, a production build and the legacy-bundle scan pass. Sign-in confirmed rendering and hydrating on a simulated iPad mini 4 running iPadOS 15.4 with a custom appearance applied.

Still deferred

next 16.0.10 → 16.3.2 spans three minor versions and stays separate so any regression remains attributable. nodemailer 7 → 9 stays held — every advisory is in the SMTP transport path production doesn't use, and 9.x falls outside the newly declared peer range.

Full changelog: v1.24.2...v1.24.3

LOTTO v1.24.2 — Custom appearances on the iPadOS 15 support floor

Choose a tag to compare

@MattGeiger MattGeiger released this 26 Aug 06:49

LOTTO v1.24.2 is a bug-fix release. Custom appearances did not render on the declared iPadOS 15 support floor, and only the two hand-authored built-in brands were exempt.

The defect

oklch() requires Safari 16.4. The declared floor is iPadOS 15, where the deployed iPad mini 4 runs Safari 15.6.

Hand-authored brand stylesheets were safe because they pass through the build, where Lightning CSS downlevels oklch() to sRGB for the browserslist floor — the compiled stylesheet contains no OKLCH at all. Runtime brand themes take a different path: they are derived per request and injected as an inline <style>, so they never reach that pipeline and shipped their OKLCH values verbatim.

Because an invalid value is dropped at computed-value time, the failure was total but selective:

Symptom Mechanism
Card, popover and modal surfaces transparent background-color: var(--card) invalid
Dark outlines around every panel --border invalid, so border-color falls back to currentColor
Toggle switches invisible track and thumb are theme tokens
Modals unreadable surface and backdrop scrim transparent, page content showing through

This affected the white-label feature on precisely the hardware it deploys to. It was not a simulator artifact.

The fix

serializeBrandThemeCss now emits each scope twice — an sRGB baseline, then the OKLCH values inside @supports (color: oklch(0 0 0)). Modern engines take the richer form; iPadOS 15 keeps the baseline. Colours inside gradients and shadows convert in place with alpha preserved. Derived values are unchanged; only serialization differs.

The obvious shorthand does not work here. --card: #fff; --card: oklch(...) fails, because custom properties are not validated at parse time — both declarations are accepted and the later always wins, with the invalidity surfacing only at var() substitution. @supports is the only correct guard.

Also fixed

npm run dev is now usable on the support floor. iOS 15 Safari refuses the Next.js hot-reload WebSocket, and because Next constructs it inside an async appBootstrap, the rejection aborted bootstrap before hydrateRoot — the app rendered but never became interactive. A development-only shim keeps the constructor from throwing. Hot reload is unavailable on that engine; production output is byte-identical.

Verification

Measured rather than inferred: on iPadOS 15.4, CSS.supports("color", "oklch(0.7 0.15 145)") returns false and the value computes to rgba(0, 0, 0, 0). color-mix() is supported there and was not implicated. After the fix, --card, --popover, --primary and --border all resolve to real colours on-device.

780 tests, lint, tsc, a production build and the legacy-bundle scan all pass. The serializer appears in zero client chunks. Confirmed on a simulated iPad mini 4 running iPadOS 15.4 in both the dev server and a production build, and validated on live hardware.

Full detail, including approaches considered and rejected, is in docs/ISSUES.md Issues 42 and 43.

Full changelog: v1.24.1...v1.24.2

LOTTO v1.24.1 — Security patch

Choose a tag to compare

@MattGeiger MattGeiger released this 25 Aug 06:46

LOTTO v1.24.1 is a security patch. It closes the two exploitable Auth.js weaknesses in LOTTO's own code rather than waiting on an upstream upgrade, and removes a development-only tool from the production dependency tree.

Production advisories: 16 → 9 (3 critical / 11 high / 2 moderate → 3 critical / 6 high)

Security

  • Authorization gate no longer fails open. src/proxy.ts is the single authorization check in front of every gated API prefix, and it tested session truthiness alone. Auth.js can resolve auth() to an error-carrying object rather than null when the configuration factory throws, so that check could treat a failed configuration as an authenticated session. The gate now requires a populated session.user. (GHSA-8fpg-xm3f-6cx3)
  • Admin email authorization resists Unicode confusables. Non-ASCII characters are screened on the raw address before normalization, and NFKC normalization now runs before validation rather than after. Confusables such as U+FF20 FULLWIDTH COMMERCIAL AT collapse into a plain @, so a check applied after normalization cannot see the characters it exists to reject. (GHSA-7rqj-j65f-68wh)
  • react-email moved to devDependencies. It is a preview CLI that no source file or script imports; the shipped runtime library is @react-email/components. This removed socket.io, engine.io, ws, minimatch, ajv, and fast-uri from the production tree, clearing 7 advisories with no runtime change.

Compatibility

No client-side code changed. Of the 48 built client chunks, 47 are byte-identical to v1.24.0; the sole difference is the inlined package.json metadata. Hydration was verified against the deployed build on a simulated iPad mini 4 running iOS 15.4, matching the declared support floor in docs/BROWSER_SUPPORT.md.

Deferred

next, next-auth, nodemailer, and sharp upgrades are held for a separate release. Each ships code into the client bundle or conflicts with a declared peer range, and requires device verification against the iOS 15 support floor. See CHANGELOG.md for the reasoning on each.

Full changelog: v1.24.0...v1.24.1

v.1.1.1 - Production Release

Choose a tag to compare

@MattGeiger MattGeiger released this 16 Jan 22:23

William Temple House Digital Raffle System v1.1.1

Release Date: January 16, 2026

Queue Progress Reliability

  • Advancing the draw position now skips tickets marked as Returned.
  • Confirmation dialogs now close immediately after confirming, even if a follow‑up error is surfaced.

Display Stability

  • Long‑running display screens now refresh the date correctly after midnight.
  • Public display polling cadence adjusted to 10 seconds (built‑in and standalone).

Feature Highlights

  • Admin actions to mark tickets as Returned or Unclaimed with validation.
  • Admin UI now includes visually distinct Lucide icons with titles
  • Returned tickets excluded from wait‑time estimates; returning the current ticket auto‑advances.
  • Unclaimed tickets can only be marked after a draw position has been called.
  • Live State sections for Returned/Unclaimed tickets.
  • Ticket status legend and ticket detail messaging on the display.
  • Sonner toast notifications for validation and error feedback.
  • Subtle status gradients on returned/unclaimed admin cards.

Versioning

  • Bumped application version to 1.1.1.

v.1.0.4 - Production Release

Choose a tag to compare

@MattGeiger MattGeiger released this 12 Dec 20:19
7ccedf6

William Temple House Digital Raffle System v1.0.4

Release Date: December 12, 2025

Authentication (OTP-First)

  • Updated the /login authentication card so One-Time Passcode (OTP) is the default sign-in method.
  • Swapped tab order so OTP appears on the left and Magic Link appears on the right (Magic Link remains available as a fallback).

Important IT Limitation (Magic Links)

  • Magic links are currently not viable in the staff environment because Microsoft Defender (CCSI) automatically inspects links in email bodies and burns the single-use token before the user clicks it.
  • Recommended and supported method: ➡️ One-Time Passcode (OTP).

Documentation

  • Added technical documentation at docs/AUTHENTICATION.md covering supported methods, domain restriction (@williamtemple.org), the Defender limitation, and the OTP flow.

Versioning & UI

  • Bumped application version to 1.0.4.
  • Updated the staff landing page to display the version from package.json (prevents stale hardcoded version strings).

Maintenance (Lint Warning Cleanup)

  • Resolved existing lint warnings by removing unused imports/variables and applying Next.js image guidance (replaced logo <img> tags with next/image on the read-only display).

Security

Fix React Server Components CVE vulnerabilities

Updated dependencies to fix Next.js and React CVE vulnerabilities.

The fix-react2shell-next tool automatically updated the following packages to their secure versions:

  • next
  • react-server-dom-webpack
  • react-server-dom-parcel
  • react-server-dom-turbopack

All package.json files have been scanned and vulnerable versions have been patched to the correct fixed versions based on the official React advisory.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>