Metadata-Version: 2.4
Name: agent-file-bridge
Version: 0.2.0
Summary: Self-hosted file handoff server for AI agents — temporary upload and download links for agent↔user file exchange
Author: wmyung
License: MIT
Project-URL: Homepage, https://github.com/wmyung/agent-file-bridge
Project-URL: Issues, https://github.com/wmyung/agent-file-bridge/issues
Keywords: ai-agent,ai-agents,file-transfer,file-handoff,upload,download,temporary-links,browser-upload,self-hosted,fastapi,sqlite,agent-file-exchange,artifact-sharing,human-in-the-loop,cli-file-sharing,agent-infrastructure
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn[standard]>=0.27
Requires-Dist: python-multipart>=0.0.9
Dynamic: license-file

# 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.

```sh
# 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.

```text
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

```bash
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:

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

## Commands

```bash
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)

```sh
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)

```sh
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`:

```json
"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:

```bash
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

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

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

```json
{"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:**

```sh
--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

```bash
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](SECURITY.md).
