Files
headquarter/openspec/changes/instance-proxy/design.md
Fusion 74b5d0dc8c feat(instance-proxy): add HTTP proxy for tool instances
Add API proxy endpoint so users can access running tool instances
through the backend API instead of internal Docker network.

Backend:
- Add container_name field to ToolInstance model
- Create /instances/{id}/proxy/{path:path} endpoint with ownership checks
- Proxy HTTP requests to containers via docker network using container names
- Support all HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
- Store proxy URL in instance.url instead of localhost
- Add Alembic migration 0007 for container_name column
- Add get_container_name() utility to docker.py

Frontend:
- Update Open button to use full proxy URL (API_BASE_URL + instance.url)

Closes instance-proxy OpenSpec change.
2026-05-20 10:37:21 +02:00

3.0 KiB

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)?