Skip to content

iili: the shortener frontend at iili.uk, on the 1d4 stack (#1359) - #1430

Merged
aaylward merged 11 commits into
mainfrom
claude/moonbase-issue-1359-45y0y2
Aug 21, 2026
Merged

iili: the shortener frontend at iili.uk, on the 1d4 stack (#1359)#1430
aaylward merged 11 commits into
mainfrom
claude/moonbase-issue-1359-45y0y2

Conversation

@aaylward

@aaylward aaylward commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Chunk 2 of #1359: domains/r3dr/apps/iili_web — a Cloudflare Worker SPA on the 1d4_web stack (Vite, React 19, Tailwind preflight, Vitest) for iili.uk, the shortener's new short domain. A second frontend for the same API ships separately as muchq.com/r3dr (muchq/muchq.github.io#277); both mint iili.uk/r/{slug} links. Springtime look: meadow whites, leaf green, blossom pink, AA-checked.

  • UI: shorten form (bare domains get https://, schemes lowercased, traits mirrored client-side in code points), expiry chips (1h/1d/7d/30d — the 30-day option sits 5 minutes under the API ceiling so a fast client clock can't turn it into a 400), copy with announced states, last-five recent links in localStorage (slug-shape validated, expiry-pruned each minute), 10s fetch timeout, friendly 429/network messages. Desktop + mobile; keyboard focus survives submit (busy, not disabled); persistent live regions.
  • Worker: GET|HEAD /r/{slug} (trailing slash tolerated; /r/, not bare /{slug} — asset paths are themselves slug-shaped) 302s to api.muchq.com/r3dr/v2/r/{slug} — the browser follows directly, so the API's per-client rate limit keys on the real client, not worker egress IPs. Everything else is the SPA.
  • v2 paths: the API's routes move to /r3dr/v2/* (model, Caddy matchers, deploy pins, UI, worker — one rename).
  • Slug-collision guard: v1 minted the same slugs from its own url_ids; v2's migrations floor its sequence at 1M and clear any pre-floor rows, so v2 ids can never alias v1's slug space (belt-and-suspenders now that iili.uk is a fresh domain — r3dr.net links die with v1 in chunk 3). Pinned by nextval/monotone/clear tests.
  • CORS: api.muchq.com moves from unconditional single-origin ACAO to per-origin echoes (muchq.com, iili.uk, localhost:5173 for vite dev) + Vary: Origin, response and preflight both. caddy validate clean; the deploy test scopes preflight vs response halves, closes the origin set, and pins Content-Type + POST in the allow-lists.
  • prom_proxy: URL-cache tiles for r3dr_v2, via the parameterized cache block Standard metric families for cross-cutting concerns (rate limiters, caches) #1209 was waiting on (portrait shares it); helpers' exact queries pinned.
  • CI: test-iili-web job (typecheck, vitest, build) mirroring test-1d4-web. The CodeQL high on stored-slug hrefs is closed by a slug-shape barrier in loadRecent + encoding in shortLink.

Verified: 43 web tests + 15 PG-gated store tests (local Postgres 16, plus a fresh-database run for the migration ordering), two review panels (4 lenses each; all surviving findings folded), 18/18 mutation checks killed, Playwright smoke in real Chromium at 1440×900 and 390×844.

Deploy is dashboard-side (like 1d4-web): Workers project iili-web, root /domains/r3dr/apps/iili_web, build npm ci && npm run build, deploy npx wrangler deploy --config dist/iili_web/wrangler.json, then attach the iili.uk custom domain — a fresh domain, so that's the whole launch. Chunk 3 (retiring r3dr.net, the VM, the Go service, and the Neon credential) stays in #1359.

🤖 Generated with Claude Code

https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8


Generated by Claude Code

claude added 3 commits August 21, 2026 19:16
Cloudflare Worker SPA replacing the Go VM's static page: shorten form
with expiry chips, copy + recent links, /r/{slug} 302 to the v2 API
keeping v1's link shape. Multi-origin CORS at api.muchq.com (pinned by
deploy test), test-r3dr-web CI job.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
Light meadow palette — warm whites, leaf green, blossom pink (the 3).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
- Floor url_ids at 1M in migrations: v1 minted the same slugs from its
  own sequence, so post-cutover v1 links must 404, not alias
- 30-day chip sits 5min under the ceiling (client clock skew)
- Worker: HEAD gets the 302, trailing slash tolerated
- 10s fetch timeout; friendly network-failure message
- Palette to AA: darker CTA/muted/expiry text, accent-deep rename
- a11y: busy-not-disabled submit (focus survives), persistent live
  regions, announced copy states, aria-pressed chips, labelled Clear
- normalizeUrl lowercases http(s); lengths count code points; in-flight
  typing survives the input clear; recent list re-prunes each minute
- VITE_SHORT_LINK_BASE for workers.dev previews
- Tests: bare-domain form path, copy paths, expiry note text, option
  table, double-submit guard, network failures; CORS test scopes
  preflight vs response echoes and closes the origin set

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
Comment thread domains/r3dr/apps/iili_web/src/components/RecentLinks.tsx Fixed
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
1d4-web 632ea6d Commit Preview URL

Branch Preview URL
Aug 21 2026, 10:29 PM

claude added 4 commits August 21, 2026 19:41
localStorage slugs render into hrefs; filter to the 3/6/11 base64url
shape on load and encode in shortLink.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
r3dr_v2 is the second aura::Cache emitter #1209 was waiting for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
Scoping alone can't tell hit rate from miss rate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
Comment thread domains/r3dr/apps/iili_web/src/__tests__/api.test.ts
Comment thread domains/r3dr/apps/r3dr_web_v2/src/components/ShortenCard.tsx Outdated
Comment thread domains/r3dr/apis/r3dr_v2/migrations.cc Outdated
Comment thread domains/r3dr/apps/iili_web/src/__tests__/ShortenCard.test.tsx
Comment thread deploy/consolidated/deploy_config_test.go
Comment thread domains/r3dr/apps/iili_web/package.json
Comment thread domains/r3dr/apps/r3dr_web_v2/README.md Outdated
Comment thread domains/r3dr/apps/r3dr_web_v2/README.md Outdated
Comment thread domains/r3dr/apis/r3dr_v2/pg_url_store_test.cc Outdated
Comment thread domains/r3dr/apps/r3dr_web_v2/src/recent.ts Outdated
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review (panel-style, fold-ins only — no separate issues)

Four lenses (correctness, tests/docs/CI, altitude; findings self-refuted then verified). Survivors are the line comments on this PR.

Highest leverage

  1. Preview path lies: hardcoded r3dr.net labels vs VITE_SHORT_LINK_BASE href/copy, and workers.dev SPA cannot mint under current CORS.
  2. Sequence floor: test does not bite is_called / GREATEST; migration does not clear pre-floor rows if chunk 1 already minted.
  3. Tests that stay green under the claimed mutation: fetch timeout signal, busy-not-disabled, CORS Allow-Methods.

Nits / simplify: slug-shape {3,11} vs server 3|6|11; README “30 days = ceiling”; Tailwind preflight-only.

Refuted / not filed: double-submit race; worker HEAD vs Caddy GET-only; site-wide ACAO for credential-less APIs; collapsing the worker→API 302 (rate-limit client identity earns keep); “one item one PR” against this #1359 chunk (aggregation intentional).

test-r3dr-web mirrors test-1d4-web — no missed gate vs that job. CI still pending on sanitize / build-and-test as of review.

- Migrations clear pre-floor rows (v1's slug space); creates before data
  fixes so a fresh DB migrates; floor test uses nextval + monotone case
- Slug shape tightened to exactly 3|6|11
- Link labels derive from the same base as hrefs
- Pin busy-not-disabled, the fetch timeout signal, CORS Allow-Methods
  POST, and the single-line unconditional-ACAO form
- README: "just under the ceiling"; honest workers.dev preview flow

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8

Copy link
Copy Markdown
Collaborator Author

Round 2 folded (latest push):

  1. Sequence floor hardened — migrations now also clear rows minted below the floor (v1's slug space; no-op forever once clean), creates run before data fixes so a fresh DB migrates, and the tests use nextval (kills an is_called flip) plus a remigrate-above-floor monotone case and a pre-seeded-row-vanishes case. Verified against a brand-new database, DELETE arm mutation-checked.
  2. Link labels derive from shortLink()'s base — no more hardcoded r3dr.net text over a preview href.
  3. Pinned: fetch init.signal is an AbortSignal; submit is aria-busy and not disabled mid-flight; CORS halves list POST in Allow-Methods; single-line unconditional-ACAO form guarded.
  4. Slug shape exactly 3|6|11 with a wrong-length rejection test.
  5. README: "just under the API's ceiling"; preview flow rewritten honestly — workers.dev can't mint under CORS, so mint from npm run dev with VITE_SHORT_LINK_BASE pointing at the preview.

Tailwind stays (see thread): preflight-only is 1d4_web's own usage; parity was the brief.


Generated by Claude Code

@aaylward
aaylward enabled auto-merge (squash) August 21, 2026 20:58
r3dr_web_v2 -> iili_web, served at iili.uk (fresh short domain — no
cutover; r3dr.net retires with v1 in chunk 3). Short links are
iili.uk/r/{slug}; CORS origin swaps r3dr.net -> iili.uk. The muchq.com
/r3dr page (muchq.github.io) mints the same links.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017HmkKHfFNgq4buWdcAFqB8
@aaylward aaylward changed the title r3dr_web_v2: the r3dr.net UI on the 1d4 stack (#1359) iili: the shortener frontend at iili.uk, on the 1d4 stack (#1359) Aug 21, 2026
@cursor

cursor Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review (round 3 @ b196521 — aggregated; fold-ins only, no separate issues)

Four lenses on the post-rebrand HEAD (correctness, data/CORS/SQL, tests/docs/CI, altitude). Findings self-refuted then verified against the tree. Round-2 folds (floor clear + nextval/monotone, labels from shortLink, AbortSignal presence, busy-not-disabled, CORS POST, slug 3|6|11, README ceiling/preview honesty) look intact; CORS origin is iili.uk.

Highest leverage

  1. wrangler.toml: SPA asset routing can swallow /r/{slug} on a routine compat-date bump. Worker’s only job is the 302 before env.ASSETS. With not_found_handling = "single-page-application" and no run_worker_first, Cloudflare’s assets_navigation_prefers_asset_serving (compat date ≥ 2025-04-01, or the flag) serves index.html for browser navigations that miss an asset without invoking the user Worker. Today’s 2024-04-03 masks this — same date 1d4 keeps because its Worker is a no-op proxy (and says so). iili’s Worker is not a no-op; a date bump (what CF docs recommend, and what the vite-plugin comment on 1d4 already tempted) turns short-link clicks into the SPA shell. worker.test.ts calls worker.fetch directly, so it stays green.
    Fold-in: run_worker_first = ["/r/*"] under [assets] (or true), comment that SPA navigations must not own /r/*, and note unit tests alone can’t see assets routing. Optionally bump the date once that’s in — don’t leave the landmine.

Medium

  1. Minute prune claimed, untested. App.tsx re-filters expiresAt every 60s; App.test.tsx only covers boot-time loadRecent filtering. Deleting the setInterval effect leaves all web tests green; expired rows stay visible until remount.
    Fold-in: fake timers including setInterval, seed a soon-to-expire link, advance 60s, assert Recent drops it.

  2. r3dr_v2 README contradicts itself. Lines 16–18 correctly point at iili_web + muchq.com/r3dr; line 28 still says Not here: UI (#1359 chunk 2, muchq.com).
    Fold-in: drop UI from that list (or name the two fronts).

  3. Floor comments still sell the dissolved cutover. migrations.cc / store-test comments narrate “once the worker owns the domain” / “alias after cutover.” After the iili.uk rebrand the same-host hazard is gone (PR body already calls this belt-and-suspenders; r3dr.net dies with v1 in chunk 3). Tests still pin useful behavior.
    Fold-in: rewrite to pre-floor v2 cleanup + optional id-space insurance. Dropping the floor entirely is a later simplify, not a block.

Nits

  1. 10s fetch timeout duration unpinned. Happy path asserts init.signal is an AbortSignal; AbortSignal.timeout(1) or 3_600_000 still greens. Spy AbortSignal.timeout / export the constant and expect 10_000.
  2. Smithy service blurb still says “Path v1 is the public contract…” while @http URIs are /r3dr/v2/* — one-line comment fix in r3dr_v2.smithy.

Refuted / not filed

Worker HEAD vs Caddy GET-only; double-submit race; Tailwind preflight-only (author already defended 1d4 parity); collapsing worker→API 302 (rate-limit client identity); proxying shorten through the worker; www.muchq.com missing from API CORS (pre-existing); CodeQL stored-slug href (shape barrier + encode); “one item one PR” vs this #1359 chunk (aggregation intentional).

CI note

test-iili-web green and mirrors test-1d4-web. Affected r3dr_v2 / prom_proxy / deploy tests passed in the latest build-and-test. That job is red on //domains/games/apis/golf_hub:pg_hub_e2e_test — unrelated to this diff; worth a rerun, not a fold-in here.

- run_worker_first=/r/* and bump compat date so SPA asset routing
  cannot swallow short-link navigations; unit tests cannot see this
- Pin the minute Recent prune with fake setInterval; pin shorten
  timeout at 10_000 via AbortSignal.timeout spy
- Drop stale "UI not here" from r3dr_v2 README; smithy blurb names
  /r3dr/v2/*; rewrite floor comments past the dissolved cutover
- setupTests: in-memory localStorage when Node's experimental one
  is undefined (local Node 26)
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 21, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
moonbase 632ea6d Aug 21 2026, 10:30 PM

2025-04-01 was only the assets-routing threshold; with run_worker_first
in place, use CF's recommended current date.
@aaylward
aaylward disabled auto-merge August 21, 2026 22:30
@aaylward
aaylward merged commit fd333b3 into main Aug 21, 2026
19 of 20 checks passed
@aaylward
aaylward deleted the claude/moonbase-issue-1359-45y0y2 branch August 21, 2026 22:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants