You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds Cloudflare Tunnel (cloudflared) as a sibling tunneling option to ngrok, and unifies both behind a single PUBLIC_TUNNEL_DOMAIN env var. Useful when an OAuth provider like QuickBooks needs a public HTTPS redirect URI but the rest of the app runs against localhost.
Either tunnel works for the OAuth handshake — pick based on what you already have set up:
Tunnel
Setup
Notes
ngrok
ngrok account + reserved static domain on *.ngrok-free.dev (free tier)
Lowest one-time setup if you don't own a domain. Free tier shows a browser interstitial on first visit per session.
More steps to set up, but no interstitial and you get your own branded hostname.
cloudflared is not a zero-setup path — it requires cloudflared tunnel login (which needs a Cloudflare account) plus a Cloudflare-managed DNS zone. The genuinely zero-config *.trycloudflare.com "quick tunnel" mode isn't used here because it produces a random URL each run, which is incompatible with Intuit's pre-registered redirect URI requirement.
Changes
New
bin/tunnel-cloudflared.sh — Runs cloudflared tunnel run $CLOUDFLARED_TUNNEL_NAME against the dev server. Validates cloudflared is installed, that CLOUDFLARED_TUNNEL_NAME and PUBLIC_TUNNEL_DOMAIN are set, and warns if the target port has no listener.
tunnel:cloudflared npm script + VS Code task mirroring the existing ngrok wiring.
Modified
next.config.js — Reads PUBLIC_TUNNEL_DOMAIN (tunnel-agnostic) instead of NGROK_DOMAIN. Mutates process.env.NEXT_PUBLIC_ROBOSYSTEMS_API_URL directly so the override applies even when .env already defines that var (the env: {} field in next.config.js doesn't override .env values that are loaded first).
bin/tunnel-ngrok.sh — Reads PUBLIC_TUNNEL_DOMAIN instead of NGROK_DOMAIN.
.env.example — Restructured into "Option A — ngrok" / "Option B — cloudflared" sections under a single PUBLIC_TUNNEL_DOMAIN placeholder.
Breaking Changes
NGROK_DOMAIN is renamed to PUBLIC_TUNNEL_DOMAIN. Anyone with an existing .env using NGROK_DOMAIN will need a one-line rename. No runtime/API breakage.
Testing Notes
End-to-end verified locally with cloudflared running against https://qb.robofinsystems.com → localhost:3001 → localhost:8000. Browser session, login, and /v1/* API proxying all confirmed working through the tunnel in Chrome and Firefox.
ngrok path still works unchanged — only the env var name shifted.
next.config.js is inert when neither var is set (no rewrites, no env override, no allowedDevOrigins entry).
Known Limitations
HMR over the tunnel — Next.js dev server rejects WebSocket upgrades from the cloudflared origin with a "malformed Unauthorized" error visible in the cloudflared logs. HMR works fine when loading the app from localhost:3001 directly; through the tunnel you'll need to manually reload. This is acceptable because the tunnel is only needed during the OAuth handshake, not for day-to-day dev.
PR Review: Add Cloudflare Tunnel as alternative to ngrok
Gather context and understand request
Read changed files
Review bin/tunnel-cloudflared.sh (new script)
Review bin/tunnel-ngrok.sh (refactored)
Review next.config.js changes
Review .env.example changes
Review package.json and .vscode/tasks.json
Post comprehensive review feedback
Overall: Solid local-dev infrastructure improvement with good shell scripting practices. A few issues to address before merging, ranging from a likely CLI syntax bug to a misleading PR description.
Issues
1. bin/tunnel-cloudflared.sh — Named tunnel --url syntax is likely wrong (High)
Line 66:
exec cloudflared tunnel --url "http://localhost:${PORT}" run "${CLOUDFLARED_TUNNEL_NAME}"
The --url flag on cloudflared tunnel is the quick-tunnel trigger — it starts a temporary tunnel with a random *.trycloudflare.com URL and doesn't take a name. Appending run <name> after a --url flag is not standard documented behavior and is likely parsed incorrectly or ignored.
For a named tunnel, the correct command is:
cloudflared tunnel run "${CLOUDFLARED_TUNNEL_NAME}"
The local service URL (port) is configured in the tunnel's ingress YAML config (~/.cloudflared/<tunnel-id>.yml), not via a CLI flag at runtime. If you want to override the port at invocation time, the config file approach is the right path.
For a quick tunnel (no account, random URL), it's simply:
These two modes are mutually exclusive. The current script attempts to combine them, which won't behave as intended. Fix this →
2. next.config.js — Redundant API URL assignment (Medium)
Lines 23–25 and 42–44 both set NEXT_PUBLIC_ROBOSYSTEMS_API_URL:
// Line 23-25 (new)if(tunnelDomain){process.env.NEXT_PUBLIC_ROBOSYSTEMS_API_URL=`https://${tunnelDomain}`}// Line 42-44 (existing, now redundant)env: tunnelDomain
? {NEXT_PUBLIC_ROBOSYSTEMS_API_URL: `https://${tunnelDomain}`}
: {},
The comment on lines 19–21 correctly explains why the process.env mutation was needed (.env wins over the env field). But that means the env: tunnelDomain ? { ... } : {} block is now dead code — the mutation already handles it, and the env field no longer takes effect for this var. The env field can be removed to avoid confusion. Fix this →
3. PR description doesn't match the implementation (Medium)
The summary says:
"Cloudflare Tunnel (free, no account required for quick tunnels)"
But the implementation requires:
cloudflared tunnel login (account required)
A Cloudflare-managed DNS zone (domain ownership required)
cloudflared tunnel create + cloudflared tunnel route dns (one-time setup)
This is actually more setup than ngrok, just without a paid plan. The genuinely zero-config path — cloudflared tunnel --url http://localhost:3001, which generates a *.trycloudflare.com URL with no login — isn't offered at all. Consider either:
Adding a second quick-tunnel script/mode that runs the zero-config path, or
Correcting the PR description and .env.example to accurately describe the named-tunnel requirement
tunnel-ngrok.sh defensively strips https:///http:// from PUBLIC_TUNNEL_DOMAIN (lines 48–49). The cloudflared script doesn't, but next.config.js does strip it (line 14–17), so no breakage — just a consistency gap between the two scripts.
Line 41: "Set it in $REPO_ROOT/.env to your reserved ngrok static domain." — now that PUBLIC_TUNNEL_DOMAIN is shared between both tunnel providers, a developer running tunnel:ngrok who has their cloudflare domain set would never see this error, which is fine. But a developer who hasn't set any domain yet and is trying ngrok gets a ngrok-specific hint, which is still correct. No action needed — just noting the shared-variable tradeoff is acceptable here.
What's Working Well
Shell scripting quality: set -euo pipefail, exec handoff, non-fatal port listener check, and set -a / set +a for .env sourcing are all solid patterns — consistent with the existing ngrok script.
Provider-agnostic abstraction: Unifying on PUBLIC_TUNNEL_DOMAIN in next.config.js is clean — adding a third tunnel provider later only touches the shell script, not the Next config.
Documentation: The .env.example expansion and inline script comments are clear and thorough.
VS Code integration: The new task correctly reuses the existing portRL input (defined in the inputs section at line 167).
Breaking change is acknowledged: The NGROK_DOMAIN → PUBLIC_TUNNEL_DOMAIN rename is documented in the PR description.
Summary: Fix the cloudflared tunnel CLI syntax (#1) before merging — it's the only change that would cause the script to silently misbehave. The redundant env field (#2) is low-risk cleanup. The description accuracy (#3) is worth a quick editorial pass.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Cloudflare Tunnel (
cloudflared) as a sibling tunneling option to ngrok, and unifies both behind a singlePUBLIC_TUNNEL_DOMAINenv var. Useful when an OAuth provider like QuickBooks needs a public HTTPS redirect URI but the rest of the app runs against localhost.Either tunnel works for the OAuth handshake — pick based on what you already have set up:
*.ngrok-free.dev(free tier)cloudflared tunnel login+cloudflared tunnel create+ DNS route (one-time)cloudflared is not a zero-setup path — it requires
cloudflared tunnel login(which needs a Cloudflare account) plus a Cloudflare-managed DNS zone. The genuinely zero-config*.trycloudflare.com"quick tunnel" mode isn't used here because it produces a random URL each run, which is incompatible with Intuit's pre-registered redirect URI requirement.Changes
New
bin/tunnel-cloudflared.sh— Runscloudflared tunnel run $CLOUDFLARED_TUNNEL_NAMEagainst the dev server. Validatescloudflaredis installed, thatCLOUDFLARED_TUNNEL_NAMEandPUBLIC_TUNNEL_DOMAINare set, and warns if the target port has no listener.tunnel:cloudflarednpm script + VS Code task mirroring the existing ngrok wiring.Modified
next.config.js— ReadsPUBLIC_TUNNEL_DOMAIN(tunnel-agnostic) instead ofNGROK_DOMAIN. Mutatesprocess.env.NEXT_PUBLIC_ROBOSYSTEMS_API_URLdirectly so the override applies even when.envalready defines that var (theenv: {}field innext.config.jsdoesn't override.envvalues that are loaded first).bin/tunnel-ngrok.sh— ReadsPUBLIC_TUNNEL_DOMAINinstead ofNGROK_DOMAIN..env.example— Restructured into "Option A — ngrok" / "Option B — cloudflared" sections under a singlePUBLIC_TUNNEL_DOMAINplaceholder.Breaking Changes
NGROK_DOMAINis renamed toPUBLIC_TUNNEL_DOMAIN. Anyone with an existing.envusingNGROK_DOMAINwill need a one-line rename. No runtime/API breakage.Testing Notes
cloudflaredrunning againsthttps://qb.robofinsystems.com→localhost:3001→localhost:8000. Browser session, login, and/v1/*API proxying all confirmed working through the tunnel in Chrome and Firefox.next.config.jsis inert when neither var is set (no rewrites, no env override, no allowedDevOrigins entry).Known Limitations
localhost:3001directly; through the tunnel you'll need to manually reload. This is acceptable because the tunnel is only needed during the OAuth handshake, not for day-to-day dev.🤖 Generated with Claude Code
Branch Info:
feature/cloudflared-tunnel-optionmainCo-Authored-By: Claude noreply@anthropic.com