## Context

Currently, tool instances run as Docker containers on the internal Docker network. The backend stores their URL as `http://localhost:{port}`, which is only accessible from inside the API container. Users clicking "Open" in the frontend get a 404 because their browser can't reach the internal container.

The API and containers share a Docker network, so the API can reach containers by their container name or IP.

## Goals / Non-Goals

**Goals:**
- Users can access running tool instances through the API via HTTPS
- Proxy enforces ownership (only instance owner can access)
- Support both HTTP and WebSocket traffic
- Minimal latency overhead
- Works with existing Docker setup

**Non-Goals:**
- Public URLs / custom domains for instances (that's Option 2/3)
- Load balancing across multiple instances
- Advanced path rewriting (just pass-through)

## Decisions

### Proxy via FastAPI route (not separate service)

**Decision:** Implement proxying as a FastAPI endpoint using `httpx` for async forwarding.

**Rationale:** 
- Keeps everything in one deployable unit
- Easy access to existing auth dependencies (`get_current_user_id`)
- Can reuse existing session cookie auth
- No extra infrastructure needed

**Alternative considered:** Separate nginx/traefik proxy service
- Rejected: adds operational complexity for a single feature

### Use container name for internal routing

**Decision:** Store container name in ToolInstance model and route to `http://{container_name}:{port}`

**Rationale:**
- Container names are stable and DNS-resolvable within Docker network
- More reliable than IPs which can change
- Already using container names in docker.py

### Path: `/instances/{id}/proxy/{path:path}`

**Decision:** All proxied traffic goes through `/instances/{id}/proxy/*`

**Rationale:**
- Clear URL structure
- Easy to apply auth middleware
- `path:path` captures everything after `/proxy/`

### WebSocket upgrade handling

**Decision:** Support WebSocket upgrade by inspecting the `Upgrade: websocket` header and establishing a bidirectional pipe.

**Rationale:**
- code-server and jupyter use WebSockets for real-time features
- FastAPI doesn't natively support proxying WebSockets, but we can use `starlette.websockets` to handle the upgrade

## Risks / Trade-offs

**[Risk]** API becomes bandwidth bottleneck for all instance traffic
→ **Mitigation:** Document this limitation. Future migration to Option 2 (Traefik labels) possible.

**[Risk]** Container name collision
→ **Mitigation:** Instance names already include UUID suffix, collision probability is negligible.

**[Risk]** Large file uploads/downloads through proxy
→ **Mitigation:** Use streaming response in httpx. Monitor memory usage.

## Migration Plan

1. Deploy backend changes (proxy endpoint + model updates)
2. Update frontend links to use proxy URL
3. Test with code-server instance
4. Monitor API performance

## Open Questions

- Should we add rate limiting to the proxy endpoint?
- Do we need to rewrite response headers (Location, Set-Cookie)?