Expose local ports to the internet with one command — powered by dynamoip and Cloudflare Tunnels.
drok http 3000
# => https://drok-a1b2c3.yourdomain.com → localhost:3000
No config files. No sudo. No ngrok accounts.
- Bun (v1.0+)
- A domain managed by Cloudflare (free tier works)
- A Cloudflare API token with:
- Zone : DNS : Edit
- Account : Cloudflare Tunnel : Edit
cloudflared(auto-installed on first run via Homebrew on macOS)
- Go to https://dash.cloudflare.com/profile/api-tokens
- Click Create Token
- Use Custom Token with these permissions:
- Zone → DNS → Edit (scoped to your domain)
- Account → Cloudflare Tunnel → Edit
- Copy the token — you'll need it for
drok login
# Clone and install
git clone <your-repo-url>
cd drok
bun install
# Link globally so `drok` is available everywhere
bun linkdrok loginThis prompts for:
- Cloudflare API Token — the token you created above
- Base domain — e.g.
yourdomain.com - Email — optional, for Let's Encrypt notifications
Credentials are saved to ~/.drok/credentials.json (mode 0600). No files are created in your project.
# Start any local server first
# e.g. python3 -m http.server 3000
# Then expose it
drok http 3000This will:
- Create/reuse a Cloudflare Tunnel
- Set a CNAME DNS record
- Start a local reverse proxy on
127.0.0.1:8080 - Print your live public URL
Open the printed URL (e.g. https://drok-a1b2c3.yourdomain.com) from any device — phone, another computer, anywhere.
Press Ctrl+C to stop.
drok --help
drok http --helpdrok login
# Enter your CF API token, domain, and optional email
# Should print: "Credentials saved to ~/.drok/credentials.json"drok config
# Should show your domain and masked token# Terminal 1 — start a test server
python3 -m http.server 8000
# Terminal 2 — expose it
drok http 8000You should see output like:
╭──────────────────────────────────╮
│ drok — exposing localhost:8000 │
╰──────────────────────────────────╯
Domain : https://drok-a1b2c3.yourdomain.com
Proxy : http://127.0.0.1:8080
✔ cloudflared ready
✔ Tunnel ready
✔ DNS configured
╭─────────────────────────────────────────────────────────╮
│ ✨ Live on the internet! │
│ │
│ https://drok-a1b2c3.yourdomain.com → localhost:8000 │
│ │
│ Press Ctrl+C to stop. │
╰─────────────────────────────────────────────────────────╯
drok http 3000 --subdomain myapp
# => https://myapp.yourdomain.comdrok http 3000 --subdomain api --domain other.com
# => https://api.other.comdrok logout
# Removes ~/.drok/credentials.json| Command | Description |
|---|---|
drok login |
Store Cloudflare credentials interactively |
drok http <port> |
Expose a local port to the internet |
drok config |
Show stored config (token masked) |
drok logout |
Remove stored credentials |
| Flag | Short | Description |
|---|---|---|
--subdomain <name> |
-s |
Custom subdomain (default: random drok-XXXXXX) |
--domain <domain> |
-d |
Override base domain from credentials |
--proxy-port <port> |
-p |
Local proxy port (default: 8080) |
Browser → https://myapp.yourdomain.com
→ Cloudflare Edge (TLS termination)
→ cloudflared daemon (outbound tunnel on your machine)
→ http://127.0.0.1:8080 (drok proxy)
→ localhost:3000 (your app)
- Uses Cloudflare Tunnel (Max mode) — no inbound ports, no firewall changes
- Tunnel credentials cached in
~/.localmap/tunnels/(reused across runs) - DNS CNAME records auto-managed via Cloudflare API
drok/
├── bin/drok.ts # Shebang entry point
├── src/
│ ├── index.ts # CLI root (citty)
│ ├── commands/
│ │ ├── http.ts # Tunnel orchestration
│ │ ├── login.ts # Interactive credential setup
│ │ ├── logout.ts # Remove credentials
│ │ └── config.ts # Display config
│ └── lib/
│ ├── credentials.ts # ~/.drok/ read/write
│ └── constants.ts # Paths, defaults
├── package.json
└── tsconfig.json
MIT