Skip to content

Repository files navigation

Agent File Bridge

Turn a local file into a temporary download link. Create a browser upload page so someone can send files back. No cloud, no accounts, no re-uploading what's already on disk.

# share a file that's already on the server
afb share ./report.docx --ttl 1h

# let someone upload files to the server
afb upload-session --ttl 1h --max-files 5

Why

Chat platforms have file-size limits, MIME blocks, and silent failures. Worst part: an agent generates a file on a server, you download it from the server, then upload to the chat. Every time.

Agent File Bridge inverts that. The agent runs a tiny server, points at a local path, and gets back a URL. No upload step. No S3 bucket. No cloud.

Agent: "I have a file at /tmp/output.docx"
  → afb share /tmp/output.docx
  → "https://files.example.com/d/abc123" (expires in 1h)
  → send that URL to anyone

Reverse direction works too — someone with a browser uploads files straight to the server's filesystem. Uploaded files get scanned for viruses before the agent touches them.

Quick start

pip install agent-file-bridge

mkdir -p ./uploads
AFB_BASE_URL=http://127.0.0.1:8765 \
AFB_ALLOWED_ROOTS=$PWD,/tmp \
AFB_UPLOAD_DIR=$PWD/uploads \
afb serve --host 127.0.0.1 --port 8765

Or install directly:

curl -fsSL https://raw.githubusercontent.com/wmyung/agent-file-bridge/main/install.sh | bash

Commands

afb serve [--host] [--port]                       # start the server
afb share <path> [--ttl] [--max-downloads]        # create a download link
afb upload-session [--ttl] [--max-files] [--max-size-mb] [--ext] [--webhook-url]  # create an upload page
afb upload-status <session-id>                    # see what files arrived

Each link is a random bearer token. Anyone with the URL can access the file until it expires or hits the download limit.

How it's different

Other tools (transfer.sh, Plik, etc.) make you upload a file to get a link. That works for humans, but it's wasted work for an agent that already has the file on disk.

Tool How it works Best for
transfer.sh, Plik Upload → link Human-to-human
Magic Wormhole CLI-to-CLI tunnel Two technical users
File Browser Full web file manager Browsing files
S3 presigned URLs Signed URL from object storage Cloud workflows
Agent File Bridge Point at a local path → link Agent-to-human, agent receiving files

Network requirements

Both sides of this tool — upload and download — depend on the same thing: the server must be reachable over HTTP. Here is what each direction needs and what changes based on your server environment.

Download (server → someone)

afb share ./report.pdf
# → https://your-server/d/s0meT0ken

Someone with that URL sends a GET request to your server. That is all. No authentication, no client setup.

Upload (someone → server)

afb upload-session --ttl 1h
# → https://your-server/u/s0meT0ken
# → open in a browser → file picker → POST to server

Same requirement as download — the server must accept TCP connections from the person holding the URL.

Environments by network exposure

Server type Public IP Needs extra tooling? Notes
VPS / cloud VM (DigitalOcean, Linode, AWS EC2, GCP, etc.) ✅ Yes Depends on firewall If the firewall allows the port, the URL works globally as-is. If ports are locked down, open the firewall or use a tunnel.
Bare-metal server with public IP ✅ Yes No Best case. afb serve --host 0.0.0.0, open the port, done.
Home server behind NAT ❌ No Yes — tunnel (ngrok, Cloudflare Tunnel, Tailscale Funnel) or port forwarding The server has no public address. A tunnel relays traffic to it.
Office / corporate network ❌ No Yes — same as home NAT, plus possibly corporate firewall rules Same problem as home NAT, often stricter egress rules.
Localhost (same machine) N/A No AFB_BASE_URL=http://127.0.0.1:8765 — only the local machine can reach it. Great for agent ↔ human on the same computer.
LAN / VPN (same private network) N/A No AFB_BASE_URL=http://192.168.x.x:8765 — anyone inside the same network can reach it. No public exposure.

What this means in practice

  • Same machine (localhost): pip install + afb serve → everything works. Agent and user share the same filesystem. This is the simplest setup.
  • Same private network (LAN / Tailscale / ZeroTier): afb serve --host 0.0.0.0 → anyone on the network can download and upload. No tunnel needed.
  • Public access from anywhere: You need a VPS or a server with a public IP, or a tunnel to a NAT-ed machine.
  • Public IP but firewall locked: Open the port in the firewall, or use a tunnel. The server already has a public address — it just needs to be reachable.

Tunnels vs direct access

A tunnel (ngrok, Cloudflare Tunnel, Tailscale Funnel) creates a public URL that relays traffic to your private server. This works everywhere but adds latency, a bandwidth limit, and a third-party dependency.

Direct access (public IP + open port) needs no relay — just the server itself. This is the production setup and what the tool assumes by default.

Summary

Upload and download are the same requirement: can the server accept an HTTP request from the person holding the URL? If yes, both work. If not, neither works. The solution is always the same — give the server a public endpoint, either by using a VPS, opening its port, or putting a tunnel in front of it.

Virus scanning

Uploaded files are sent through ClamAV if clamd is running on the server. Files over 200 MB are skipped (threshold configurable via AFB_SCAN_MAX_SIZE_MB). Scan results appear in upload-status:

"scan": {"status": "clean", "detail": ""}

Possible statuses: clean, infected, unscanned (clamd not configured or file too large), error (clamd unreachable or timed out).

Set AFB_CLAMAV_SOCKET to your clamd socket path (e.g. /var/run/clamav/clamd.ctl) to enable scanning.

Upload webhook

When a user finishes uploading files, Agent File Bridge can POST the results to a URL. Set AFB_UPLOAD_WEBHOOK_URL globally, or pass --webhook-url per session:

afb upload-session --webhook-url http://localhost:8888/upload-callback

The webhook receives {"session_id": "...", "files": [...]}. Errors are non-fatal — the upload always succeeds, the webhook is fire-and-forget.

HTTP API

POST /api/share — create a download link

{"path": "/tmp/file.docx", "ttl_seconds": 3600, "max_downloads": 1}

POST /api/upload-sessions — create an upload page

{"ttl_seconds": 3600, "max_files": 5, "max_size_mb": 500, "webhook_url": "..."}

GET /api/upload-sessions/{id} — check what was uploaded

Returns file paths, sizes, SHA-256 hashes, MIME types, and scan results.

For agents

Three patterns.

Send a file. Call afb share, get a URL, send to the user. Don't expose the local path.

Receive files. Call afb upload-session, send the URL, poll afb upload-status. Check SHA-256 and scan status before processing.

Defaults:

--ttl 1h
--max-downloads 1
--max-files 5
--max-size-mb 500
--ext .pdf,.docx,.xlsx,.csv,.zip,.png,.jpg,.txt

Config

Variable Default
AFB_BASE_URL Public URL shown in generated links
AFB_SERVER http://127.0.0.1:8765 CLI target
AFB_ALLOWED_ROOTS Directories that can be shared (comma-sep)
AFB_UPLOAD_DIR ./uploads Where uploaded files land
AFB_DB ./agent-file-bridge.sqlite3 Database path
AFB_TOKEN_BYTES 24 Random token length
AFB_CLAMAV_SOCKET Path to clamd socket (enables scanning)
AFB_SCAN_MAX_SIZE_MB 200 Skip virus scan for files over this size
AFB_UPLOAD_WEBHOOK_URL Default webhook URL for upload notifications

Upgrade

pip install --upgrade agent-file-bridge

Schema migrations run automatically on server start. Back up your .sqlite3 file before upgrading.

Security

Every URL is a bearer secret. Run behind HTTPS. Restrict AFB_ALLOWED_ROOTS. Use short TTLs. Never execute uploaded files. Enable ClamAV for untrusted uploads.

See SECURITY.md.

About

File handoff server for AI agents: temporary browser upload and download links

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages