Agent Instant Drop
Drop any file, app, or prototype to your human. Password-protected by default. Use --public to opt out.
Invoking
drop: calldropdirectly. It builds its own venv on first run, so there's no separate setup step.
Quick Start
# Start the static server (once)
drop start
# Publish a static page — auto-generated password
drop add ./report.html --desc "Weekly report"
# → http://192.168.1.50:8080/p/abc.../
# → Password: xK9mP2qLx
# Explicit public page
drop add ./public.html --public
# Custom password
drop add ./secret.html --password mysecret
# Register an app — basic auth is added automatically
drop add ./app --run "flask run --host 127.0.0.1 --port 5000" --port 5000 --name api
# → App registered
# → Auth: basic (drop / 8wK2mP4qLxNR)
# Public app (no auth)
drop add ./demo --run "python -m http.server 7777 --bind 127.0.0.1" --port 7777 --public
# Custom basic auth credentials
drop add ./app --run "..." --port 5000 --auth basic:admin:s3cret
# Run the app (spawns app + proxy + cloudflared tunnel atomically)
drop start api
# → App started: https://abc.trycloudflare.com/
# → Auth: basic (drop / <see add output>)
# Tail its logs
drop logs api # app log
drop logs api --proxy # proxy log
drop logs api --tunnel # cloudflared log
drop logs api -f # follow mode
# Stop
drop stop api
Commands
| Command | What |
|---|---|
drop start [--port N] |
Start static server (default: 8080) |
drop start <name> |
Start a registered app |
drop start <name> --auth-insecure |
Allow cleartext basic auth (skip tunnel requirement) |
drop stop / drop stop <name> |
Stop server / app |
drop status |
Health/URL overview: server state, the resolved public URL to share (+ its source), tailnet URL, cloudflare tunnel + liveness probe, page count |
drop add <path> |
Publish (password-protected by default) |
drop add <path> --public |
Publish without password |
drop add <path> --run "cmd" --port N |
Register an app |
drop list / drop list -a |
List pages from cwd / all pages |
| `drop logs [--proxy | --tunnel] [-f]` |
| `drop remove <id | name>` |
drop cleanup |
Remove entries whose source file no longer exists |
drop index-password [PASS] |
Set/clear the dashboard (/) password (--clear disables it) |
Before you hand a link to your human: the URL
drop addprints is the resolved public base. If it's a127.0.0.1loopback (drop couldn't determine a shareable address), rundrop statusto get the real tailnet/tunnel URL and to confirm the cloudflare tunnel is alive (a quick-tunnel can be a zombie: process up, URL dead). For a stable deployment, set the authoritative URL once viaDROP_PUBLIC_URLor~/.drop/base_url(e.g. yourtailscale serveHTTPS URL) —drop addthen prints that.
Flags for drop add
--name <slug>/-n <slug>— short slug used in URLs and as a stable handle--desc <text>/-d <text>— description fordrop list--password [PASS]/-p [PASS]— static-page password. Default: auto-generated.--public— opt out of default auth. Apps and pages.--run <cmd>/-r <cmd>— command to spawn (makes the entry an app)--port <N>— app port (required with--run)--auth basic[:user:pass]— explicit basic auth for apps (default: auto-gendrop:<12char>)--rewrite-host— proxy rewriteshttp://localhost:<port>in text bodies (for SPAs that hardcode it)--allow-side-door— apps only: start even if the app binds a public interface (auth then protects the tunnel only, not the direct app port)--base-url <url>— override the printed base URL (e.g. your tunnel/tailnet URL) when auto-detection can't reach it
Flags for drop start
--port <N>— server port (only when starting the static server, default 8080)--host <ip>— override detected IP for URL output--no-tunnel— skip cloudflared--auth-insecure— allow cleartext basic auth without tunnel (override)
Auth model
Static pages use a cookie-form login. Submit password → cookie set (15 min, httponly) → content served. Auto-generated passwords are 10 chars. Rate limit: 5 attempts/min per source IP, plus a global cap of 30/min per page (the source IP is the real TCP peer, never a client-supplied X-Forwarded-For).
Dashboard (/) does not list your pages unless you set a dashboard password with drop index-password. Without one, / refuses to enumerate; with one, it prompts for that password (same cookie-form login) before showing the listing.
Apps sit behind a thin reverse proxy that does HTTP basic auth. Proxy:
- Validates path starts with
/(SSRF guard) - Rejects
Upgrade(WebSocket) requests with 501 (V1 limitation) - Passes through HTTP/1.1 GET/POST/etc and 30x redirects
- With
--rewrite-host: replaceshttp://localhost:<app_port>in text response bodies with the tunnel origin (for SPAs)
Important: side door
The proxy guards itself. Your app's own port still listens wherever you bound it. If you bind 0.0.0.0 on a public IP, the app port is reachable bypassing the proxy — drop refuses to start in this case (probe after spawn). Override with --allow-side-door in your Page registration (advanced; CLI flag pending).
Best practice: always bind apps to 127.0.0.1 when using --auth:
drop add ./api --run "flask run --host 127.0.0.1 --port 5000" --port 5000
--auth requires a tunnel
basic-auth credentials transmitted over plain HTTP travel in base64 (cleartext). v2 refuses to start an auth app without a tunnel unless you pass --auth-insecure.
Tunnel (NAT + HTTPS)
drop uses cloudflared quick-tunnels to give your app an HTTPS URL.
One-time setup: tunnels need
cloudflared(and, on Linux, a background systemd service). Rundrop-install-envonce to install them into~/.drop/(a stable location that survives plugin updates).dropwill also tell you when a tunnel command needs it. Tear it all down — service, cloudflared, venvs — withdrop-uninstall-env.
- Behind NAT: tunnel auto-starts.
- Apps with
--auth: tunnel always attempted (HTTPS termination for basic auth). - cloudflared logs land in
~/.drop/logs/<page_id>.tunnel.log— no PIPE-buffer hangs (v1 bug fixed). - No automatic restart: if cloudflared crashes after
drop start,drop stop <app> && drop start <app>to recover (V3 backlog: named tunnels + watchdog process).
Publishing Directories (Manifest Required)
cat > ./project/.drop-publish << 'EOF'
index.html
assets/**
EOF
drop add ./project/
Manifest patterns: exact files (index.html), single-segment globs (*.html matches top-level .html only — it does not cross /, so it won't publish private/x.html), directory prefixes (assets / assets/), and recursive globs (assets/**, or bare ** for everything).
.env, .env.*, and .envrc files are always blocked, even if in the manifest (except .env.example).
Tips
cdto project first, thendrop add .— paths are stored absolute;drop listfilters by cwd.- Always bind apps to
127.0.0.1with--auth— side door is real. - Save the auto-generated credentials — printed once at
drop add; only the hash is stored. - Use
drop logs <name> -fto follow a misbehaving app's stdout/stderr. drop add ... --name Xis unique; second call with same name fails fast.