The API container needs access to /var/run/docker.sock to run
docker compose commands for tool instances. Add group_add to
match the host's docker GID.
Error was:
permission denied while trying to connect to the docker API at unix:///var/run/docker.sock
The compose template uses {{TOOL_NAME}} for container_name but
we weren't passing it in the variables dict, causing YAML parse error.
Error was:
yaml: cannot use 'map[string]interface {}{"TOOL_NAME":interface {}(nil)}' as a map key
Add comprehensive logging to trace 500 error:
- Log each step of docker compose up (returncode, stdout, stderr)
- Log container ID and name after start
- Log tool type and port being used
- Log each step of Cloudflare tunnel creation with API responses
- Log cloudflared config updates
This will help identify exactly where the failure occurs.
Cloudflared was hardcoded to route to port 8080, but containers
listen on different ports (8443 for code-server, 8888 for jupyter).
- Add instance_port parameter to create_tunnel and update_cloudflared_config
- Fetch tool type default_port when creating tunnels
- Route to correct internal port instead of hardcoded 8080
- Backend now returns stopped and error sessions too
- Active sessions include running/building/pending
- Recent sessions show stopped/error only
- Status badge shows actual status (running/building/pending)
- Fix Recent Sessions section to use anchor tag linking to instance URL
- Fix Last Session section to show URL and open it directly
- All Open buttons now link directly to Cloudflare URLs instead of navigating to project
- Show Cloudflare URL in active session cards
- Change Open button to anchor tag linking directly to instance URL
- Add CSS styling for URL display in session cards
- Falls back to project navigation if no URL available
Update handleOpen in SessionsPage to open the instance URL
in a new tab when available, instead of navigating to the
project page. Falls back to project navigation if no URL.
Backend:
- Add cloudflare_tunnel.py service for creating/deleting tunnels via Cloudflare API
- Add public_url and tunnel_id fields to ToolInstance model
- Update start_instance to create Cloudflare tunnel after container starts
- Update stop_instance to delete tunnel before stopping container
- Update delete_instance to cleanup tunnel before deletion
- Update restart_instance to recreate tunnel on restart
- Create Alembic migration 0011 for tunnel fields
- Add Cloudflare config settings (API token, zone ID, account ID, base domain)
Infrastructure:
- Add cloudflared service to docker-compose.traefik.yml
- Mount shared cloudflared_config volume between API and cloudflared containers
- Add Cloudflare env vars to API service
Frontend:
- Update instance Open button to handle both full URLs and proxy paths
The instance URL is now set to the Cloudflare tunnel public URL when available,
falling back to the API proxy path if tunnel creation fails.
The proxy was using instance.port which is a dynamically allocated
host port (e.g., 10001). But containers communicate on the Docker
network using their internal ports (8443 for code-server, 8888 for
jupyter). This caused connection failures when opening instances.
- Add default_port field to ToolType model (null for terminal-only tools)
- Create migration 0010 for default_port column
- Update seed data: code-server=8443, jupyter=8888, opencode=null
- Update proxy to use tool type's default_port instead of instance.port
- Update frontend ToolType interface to include default_port
Fixes: Opening instances now routes to correct internal container port
Wrap setAllSessions in useCallback so it has a stable reference.
This breaks the infinite re-render loop that was causing 4-10
requests per second to /users/me/sessions.
- Install docker-ce-cli and docker-compose-plugin in API Dockerfile
- Mount /var/run/docker.sock into API container
- Add appuser to docker group for socket permissions
- Fixes FileNotFoundError when deleting instances
The state/sessions.tsx Session interface was missing repository_id and
project_id fields that were added to api/sessions.ts in the previous
commit. This caused a TypeScript build error when the app-shell tried
to pass API sessions to the state context.
Settings save:
- Remove exclude_none=True from user_config.py model_dump() call
- Fixes fields not updating when cleared or set to null/undefined
Session deletion:
- Add project_id and repository_id to get_user_sessions response
- Update frontend Session interface with new fields
- Fix handleDelete to use IDs instead of names, resolving 404 errors
- Create ToolConfigsPage with tool type selector, config list, and add/edit form
- Support both env and file config types
- Add route /tool-configs and navigation item
- Update API client with tool config endpoints
- Build passes successfully
Add support for tool categories, interface types, and per-tool configuration.
Backend:
- Add category and interfaces fields to ToolType model
- Create ToolConfig model for storing tool-specific settings
- Add tool_configs API endpoints (CRUD)
- Update built-in tool types with categories and interfaces:
- code-server: editor, [web]
- jupyter-notebook: notebook, [web]
- opencode: ai-assistant, [terminal]
- Update instance API to include tool type interfaces
- Create Alembic migrations 0008 and 0009
Frontend:
- Update ToolType and Session interfaces with new fields
- Conditionally show Open/Terminal buttons based on tool interfaces
- Add API client for tool configs
OpenSpec: tool-config-management change created and implemented.
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.
- Add INSTANCE_BASE_PATH config option (defaults to /data/instances)
- Update docker.py to use configured path instead of hardcoded 'data/instances'
- Update Dockerfile to create /data/instances and chown to appuser
- Add instance_data volume to docker-compose.traefik.yml and docker-compose.yml
- Set INSTANCE_BASE_PATH env var in both compose files
This fixes the PermissionError when creating tool instances because
appuser can now write to /data/instances.
- Add API base URL to CORS allowed origins alongside web base URL
- Add CORS origin logging on startup for debugging
- Wrap instance creation in try/except with detailed error logging
- Return proper error message instead of raw 500 for instance creation failures
This fixes CORS errors when the frontend makes cross-origin requests and
provides better diagnostics for instance creation failures.
The OAuth callback was setting the session cookie on the 'response'
parameter but returning a brand new RedirectResponse, causing the
cookie to be lost. This created an infinite login loop where the
callback succeeded but /auth/me always returned 401.
- Set cookies on the RedirectResponse instead of the unused response param
- Remove unused 'response: Response' parameter from callback handler
- Fixes login loop in production with cross-domain cookies
- Add RequestValidationError handler to log validation errors
- Add extra=ignore to CreateInstanceRequest to be more lenient
- Add logging to create_instance endpoint to see received data
- Add missing logger import in tool_instances.py
- Apply theme immediately when saving settings (fixes theme not updating)
- Add dark mode CSS variables for success/warning/danger/info colors
- Fix shell-header background for dark mode
- Fix URL validation styles to use CSS variables
- Add explicit background/color to form inputs for dark mode support
- Quality gates: typecheck OK, lint OK, build OK
- Add Sessions tab to navigation between Dashboard and Projects
- Show active session count badge in navigation
- Create SessionsPage with:
- Last session section with resume button
- Active sessions grid with open/stop actions
- Recent sessions list
- Create session form with project/repo/tool selectors
- Add last_session_id to user config
- Update UserConfig schemas (backend and frontend)
- Add comprehensive CSS for sessions page
Quality gates: typecheck ✓, lint ✓, build ✓
The create_instance endpoint was expecting tool_type_id and display_name
as query parameters, but the frontend sends them in the JSON body.
Added CreateInstanceRequest Pydantic model to properly parse the request body.
Fixes 422 Unprocessable Content error on instance creation.
- Create enhanced health endpoints with /health and /health/db
- Add comprehensive docstrings to all API endpoints
- Add Pydantic response models with Field descriptions
- Create apps/api/README.md with setup guide
- Create ADR-001 for session auth decision
- Create ADR-002 for async SQLAlchemy decision
- Quality gates: Python syntax OK, TypeScript OK
Backend:
- Create ToolInstance model with status tracking
- Add Alembic migration for tool_instances table
- Create Docker service for compose template rendering and container execution
- Add CRUD API endpoints for tool instances
- Add lifecycle endpoints (start/stop/restart)
- Add user sessions endpoint for navigation
- Register routers in main.py
Frontend:
- Create SessionsProvider with React context
- Create sessions API client
- Update AppShell with sessions section in navigation
- Add session status indicators and polling
- Add CSS for session navigation
Quality gates: typecheck ✓, lint ✓, build ✓
- Add page-specific responsive styles for all pages
- Mobile form full-width inputs
- Stack project/repository cards on mobile
- Git toolbar wrapping on mobile
- Dashboard grid single column on mobile
- Settings layout stacking on mobile
- Touch target verification (min 44px)
- Table horizontal scroll wrapper
- SSH key list responsive layout
- Text overflow prevention in cards
Quality gates: typecheck ✓, lint ✓, build ✓
- Change workspace-main overflow from auto to hidden to prevent nested scrolling
- Change file-editor-content overflow from auto to hidden
- Change code-block overflow from auto to visible
- Add flex display to workspace-main and file-editor-content for proper height distribution
Fixes double scrollbar issue in file editor workspace
- Remove useless git diff --numstat call that failed in bare repos
- Use raw bytes instead of text decoding to avoid encoding issues
- Properly check subprocess return codes
Fixes false positive binary detection for text files like .env.sample
- Mark all tasks complete for smart-git-url-parsing change
- Mark all tasks complete for documentation-overhaul change
- Add CHANGELOG.md with feature history
- Quality gates: code previously verified in earlier sessions
- Remove duplicate WorkspaceHeader render (was showing twice)
- Update empty state 'Add Repository' link to point to
/projects/{id}/settings/repositories instead of old route
- Quality gates: typecheck ✓ lint ✓ build ✓
Frontend workspace was calling GET /projects/{id} which didn't exist,
causing 405 errors and preventing WorkspaceHeader from rendering.
Add get_project endpoint that returns a single project by ID with
ownership verification.