Skip to content

feat(tips): gate the chain switcher on a runtime TIPS_CHAINS allowlist - #78

Open
montycheese wants to merge 1 commit into
mainfrom
feat/tips-chain-gating
Open

feat(tips): gate the chain switcher on a runtime TIPS_CHAINS allowlist#78
montycheese wants to merge 1 commit into
mainfrom
feat/tips-chain-gating

Conversation

@montycheese

Copy link
Copy Markdown
Contributor

What

The TIPS chain switcher offered Base Mainnet, Base Sepolia, and Zeronet in every
deployment. The internal deployment does not serve the same chains in every
environment — production reaches the mainnet and sepolia bundle-history buckets,
development reaches zeronet — so production showed a Zeronet tab with no data
behind it.

This gates the switcher (and the TIPS API) on a TIPS_CHAINS allowlist:

environment TIPS_CHAINS chains offered
internal production mainnet,sepolia Base Mainnet, Base Sepolia
internal development mainnet,sepolia,zeronet all three
local dev / unset all three (unchanged)

Why it is runtime config, not a build flag

NEXT_PUBLIC_DEPLOY_TARGET can't express this. It is inlined into the bundle at
build time, and the internal target builds one image that is promoted from
development to production — the two environments differ only in the runtime env
the Helm chart sets. A build-time flag would have to be the same in both. The
chart already varies the per-chain TIPS_* vars this way; TIPS_CHAINS joins
them.

How

  • app/tips/enabledChains.ts — resolves the allowlist server-side. Server-only:
    TIPS_CHAINS is deliberately not a NEXT_PUBLIC_ var.
  • app/tips/components/TipsChainsProvider.tsx — carries the list into the client
    tree, which can't read the env itself. The section layout resolves it once per
    request.
  • ChainToggle renders only the enabled chains, and hides itself when there is
    only one (a single tab is a label, not a control).
  • useTipsChain clamps ?chain= to the enabled list, so a shared zeronet link
    opened against production reads as mainnet instead of erroring.
  • The five TIPS API routes 404 a chain this deployment doesn't serve. Falling
    back to the default chain would answer under the wrong chain's name, and
    letting it through would hit per-chain config that is unset here — reading the
    default bucket and either 500ing or, worse, succeeding against the wrong
    source.
  • Unset means every known chain, so nothing changes for local dev or any
    deployment that doesn't set it. A typo'd value is treated as unset rather than
    emptying the section.

Testing

app/tips/chains.test.ts covers allowlist parsing (whitespace, case,
duplicates, unknown names, empty) and the clamping fallbacks, including the
production case of a zeronet link resolving to mainnet.

  • npm run typecheck — clean
  • npx vitest run — 19 files, 146 tests pass
  • npm run build:internal and npm run build — both exit 0
  • npm run docs:check — all current

Verified in the browser against a dev server, three configurations:

run chain tabs /api/tips/blocks?chain=zeronet
TIPS_CHAINS=mainnet,sepolia Base Mainnet, Base Sepolia 404 {"error":"Not found"}
TIPS_CHAINS=mainnet,sepolia,zeronet all three passes the guard
unset all three passes the guard

With the production allowlist, /tips?chain=zeronet renders with Base Mainnet
selected and no Zeronet tab.

Follow-up

This has no effect until the internal chart sets TIPS_CHAINS per environment;
that is a separate PR in the internal deploy repo, alongside the image bump.

The internal deployment serves different chains per environment: production
reaches the mainnet and sepolia bundle-history buckets, development reaches
zeronet. Until now the switcher offered all three everywhere, so production
showed a Zeronet tab it has no data for.

NEXT_PUBLIC_DEPLOY_TARGET cannot express this. It is inlined at build time,
and the internal target builds one image that is promoted from development to
production — the two environments differ only in runtime env. So this reads a
new TIPS_CHAINS allowlist per request instead:

- app/tips/enabledChains.ts resolves it server-side (server-only: TIPS_CHAINS
  is not a NEXT_PUBLIC_ var).
- TipsChainsProvider carries the list into the client tree, which cannot read
  the env itself; the section layout resolves it once per request.
- ChainToggle renders only the enabled chains, and hides itself entirely when
  there is only one.
- useTipsChain clamps `?chain=` to the enabled list, so a shared zeronet link
  opened against production reads as mainnet rather than erroring.
- The TIPS API routes 404 a chain this deployment does not serve, rather than
  falling through to per-chain config that is unset here — which would read
  the default bucket and either 500 or answer from the wrong source.

Unset means every known chain, so local dev and any deployment that does not
set it behave exactly as before.
@cb-heimdall

Copy link
Copy Markdown
Collaborator

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@vercel

vercel Bot commented Aug 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
omni-ui Ready Ready Preview Aug 19, 2026 2:33pm

Request Review

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.

2 participants