Self-hosted file handoff server for AI agents — temporary upload and download links for agent↔user file exchange
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agent_file_bridge-0.2.0.tar.gz.
File metadata
- Download URL: agent_file_bridge-0.2.0.tar.gz
- Upload date:
- Size: 15.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e8dbcb7b5298f59ce41a1af3a64ba229ebfa454e4d9641fdf4fa9c062f4da69
|
|
| MD5 |
28fd349aeeda56d818efa7989242c032
|
|
| BLAKE2b-256 |
0204c61237307261df6d0ca3a8bb077d04b2eb644afdd290e550bf9a92144617
|
File details
Details for the file agent_file_bridge-0.2.0-py3-none-any.whl.
File metadata
- Download URL: agent_file_bridge-0.2.0-py3-none-any.whl
- Upload date:
- Size: 12.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b38b676e791f17557fd7501c95b4bc9ace2a82bc0b036af5cc53e990a3349fa1
|
|
| MD5 |
c469717aab5d064e4f294f74c8490eb5
|
|
| BLAKE2b-256 |
ce7f002fb735e96a9ee39eb506bbba84de9f54137985eb4edf15d125c3528160
|