The dev tunnel
Give an http bot a public address while you work on it locally. Covers installing cloudflared, the quick tunnel seedcord dev opens by default, using a URL you already serve, turning the tunnel off, and what happens when it fails.
Discord delivers interactions by posting them to a public URL. Your laptop doesn't have one, so Discord can't reach localhost:3000 on your machine. Without a tunnel you'd deploy every change to a server, or set up port forwarding and paste a new endpoint into the developer portal each time. seedcord dev gives your http bot a public address and writes it into your Discord application for you.
Http only
A gateway bot opens its own connection to Discord, so it ignores the tunnel setting.
What cloudflared does
cloudflared is a small program from Cloudflare. You run it on your machine. It opens a connection to Cloudflare, and Cloudflare gives you a public https address. Anything posted to that address comes back through the same connection to your local port.
Your router and firewall stay as they are, since your machine only makes outgoing connections.
You install cloudflared once. After that, seedcord dev starts it, reads the address, and stops it again when you quit.
Installing it
| platform | how to install |
|---|---|
| macOS | brew install cloudflared |
| Windows | winget install -e --id Cloudflare.cloudflared |
| Linux | Cloudflare's package repository, a .deb, an .rpm, or a direct binary |
Cloudflare's downloads page (opens in a new tab) lists every option, including Docker. seedcord looks for cloudflared on your PATH, so any install that puts it there works.
Without cloudflared your bot still runs. The terminal prints an install hint, and seedcord dev continues without a public address.
The default, a quick tunnel
A quick tunnel works without a Cloudflare account or a domain, so leaving tunnel unset gives you one on every run. Writing the endpoint uses your bot's token, which seedcord reads from DISCORD_BOT_TOKEN.
Each start follows these steps.
- Your bot's interactions server starts listening on its port.
- seedcord starts
cloudflaredpointed at that port, then waits up to 60 seconds for Cloudflare to assign a hostname. - seedcord waits four more seconds, because Cloudflare publishes the DNS record a couple of seconds after it assigns the hostname. Discord's resolver keeps a failed lookup for about half an hour, so an early check would burn that hostname for Discord while the address answered fine from your machine.
- seedcord posts an unsigned request to the new address until it gets a
401. After 60 seconds seedcord continues either way. Your bot rejects an unsigned request with exactly that status, so a401means the tunnel reaches your port. - seedcord writes the address into your Discord application as its interactions endpoint.
Cloudflare assigns a fresh hostname every run, so the last step writes a new endpoint each time. When you quit, seedcord empties the interactions endpoint field on your Discord application, since that hostname stops working once the tunnel closes.
When you already have a URL
A quick tunnel changes your endpoint on every run, and each run takes a few seconds to set up. When you want an address that stays the same, give tunnel an https URL you already serve. A named cloudflared tunnel, a tailscale funnel, a reverse proxy, or a staging server all work.
import { defineConfig } from 'seedcord';
export default defineConfig({
root: './src',
instance: './bot.ts',
entry: './index.ts',
tunnel: 'https://bot.example.com'
});seedcord doesn't start cloudflared for https://bot.example.com, so you point that address at your bot's port yourself.
seedcord runs the same 401 check, then writes the endpoint only when your application holds a different value. Your address keeps working between runs, so quitting leaves that field on your application as it is.
Turning it off
Set tunnel to false to skip all of it. Use this when you set the endpoint yourself in the developer portal, or while you work on a part of your bot that runs without a click, like a migration, a subscriber, or the shape of a card.
When it fails
Your bot keeps running through each of these, and the error goes to the cli channel.
| what happened | the error in your log |
|---|---|
| cloudflared didn't report a hostname within 60 seconds | CliTunnelUrlUnavailable |
your own URL didn't answer the 401 check within 60 seconds | CliTunnelUnreachable |
| Discord refused the address | CliTunnelNotVerified |
| cloudflared exited after going live | cloudflared exited, so the bot has no public endpoint |
The last row also turns the tunnel's status in the sidebar to lost.
CliTunnelNotVerified usually has one cause. Discord checks an endpoint by posting its own signed ping to it, and that ping fails until Discord's DNS resolver can resolve the new hostname. Pasting the same address into the developer portal keeps failing for about half an hour, since Discord's resolver holds the failed lookup that long. Press r to restart, which gets you a fresh hostname and another attempt.