docs(openspec): add OpenSpec changes for FN-005, FN-006, FN-008, FN-009, FN-010
- Add frontend-foundation change (FN-005) with 46 tasks - Add deployment-config change (FN-006) with 27 tasks - Add runfusion-poc/opencode-poc change (FN-008) with 25 tasks - Add config-secrets change (FN-009) with 31 tasks - Add codeserver-spawn change (FN-010) with 38 tasks - Include project specsheet and configuration - Archive completed deployment-config change
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-14
|
||||
@@ -0,0 +1,67 @@
|
||||
## Context
|
||||
|
||||
code-server is a VS Code instance running in a browser. The platform needs to spawn it as a Docker container with proper mounts, auth, and routing. This builds on the tool registry (FN-003) and deployment config (FN-006).
|
||||
|
||||
Current state:
|
||||
- code-server manifest exists in apps/api/app/tools/manifests/code-server.yml
|
||||
- ToolInstance model exists with status field
|
||||
- No spawn orchestration logic
|
||||
- No frontend UI for spawning
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Spawn code-server containers via Docker Compose
|
||||
- Mount user workspace, configs, secrets, and SSH keys
|
||||
- Route via Traefik subdomain
|
||||
- Track container status (creating, running, stopped, error)
|
||||
- Provide spawn UI in frontend
|
||||
|
||||
**Non-Goals:**
|
||||
- Support for other IDEs (deferred post-MVP)
|
||||
- Container resource limits (CPU/memory) - basic only
|
||||
- Automatic workspace backup
|
||||
- Multi-instance load balancing
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Docker Compose API for container management**
|
||||
- Rationale: Higher-level than Docker SDK, handles networking and volumes declaratively
|
||||
- Alternative: Docker SDK directly - more control but more complex
|
||||
|
||||
**2. code-server runs with platform auth proxy**
|
||||
- Rationale: Don't manage separate code-server passwords. Traefik middleware handles auth.
|
||||
- Implementation: Traefik forwardAuth to platform API for session validation
|
||||
|
||||
**3. Workspace mounted from host directory**
|
||||
- Rationale: Persistent storage between restarts. Easy backup.
|
||||
- Path: `/data/workspaces/{user_slug}/{project_slug}`
|
||||
|
||||
**4. SSH keys mounted as read-only volume**
|
||||
- Rationale: code-server needs Git access but shouldn't modify keys
|
||||
- Mount: `/home/coder/.ssh/` with 0400 permissions
|
||||
|
||||
**5. Spawn is synchronous (blocking) API**
|
||||
- Rationale: Simpler UX. Container creation is fast (< 5s).
|
||||
- Alternative: Async with polling - more complex, unnecessary for MVP
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Docker socket exposure is a security risk**
|
||||
→ Mitigation: Run API with limited Docker access. Consider Docker socket proxy in production.
|
||||
|
||||
**[Risk] Container failures leave dangling resources**
|
||||
→ Mitigation: Implement cleanup on error. Periodic garbage collection of orphaned containers.
|
||||
|
||||
**[Risk] code-server auth bypass**
|
||||
→ Mitigation: Disable code-server auth (PASSWORD: ""). Rely entirely on Traefik forwardAuth.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration. New feature.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we pre-pull Docker images or let Compose handle it?
|
||||
2. Do we need container health checks before marking as "running"?
|
||||
3. Should spawned containers auto-stop after inactivity?
|
||||
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
Tool registry (FN-003) and deployment config (FN-006) are prerequisites for spawning tools. code-server is the primary user-facing tool in MVP. Without a spawn flow, users cannot launch development environments, which is the core value proposition.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **code-server manifest refinement**: Update the built-in manifest with proper Docker image, ports, volumes, and config options
|
||||
- **Spawn flow API**: Backend endpoint that creates a tool instance, generates Docker Compose service, and starts the container
|
||||
- **Frontend spawn UI**: Form for selecting tool, project, and optional config overrides
|
||||
- **Runtime integration**: Mount workspace, configs, secrets, and SSH keys into the code-server container
|
||||
- **Auth proxy**: Ensure code-server is protected behind the platform's auth (no separate code-server password)
|
||||
- **Status tracking**: Poll container status and expose it via API
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `tool-spawn-api`: Backend endpoint for spawning tool instances
|
||||
- `codeserver-manifest`: Refined code-server manifest with runtime configuration
|
||||
- `spawn-ui`: Frontend form for tool selection and spawn configuration
|
||||
- `container-lifecycle`: Start, stop, and status tracking for tool containers
|
||||
|
||||
### Modified Capabilities
|
||||
- None (extends existing tool registry)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/api/app/tools/manifests/code-server.yml**: Updated manifest
|
||||
- **apps/api/app/routers/tool_instances.py**: Spawn endpoint enhancements
|
||||
- **apps/api/app/services/spawn.py**: New spawn orchestration service
|
||||
- **apps/web/src/**: New spawn UI components
|
||||
- **docker-compose.yml**: May need updates for Docker socket access
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: code-server manifest defines runtime configuration
|
||||
The system SHALL provide a complete code-server manifest.
|
||||
|
||||
#### Scenario: Manifest includes Docker configuration
|
||||
- **WHEN** the code-server manifest is loaded
|
||||
- **THEN** it specifies the Docker image (codercom/code-server)
|
||||
- **AND** it defines exposed ports (8080)
|
||||
- **AND** it defines volume mounts (workspace, config, ssh)
|
||||
|
||||
#### Scenario: Manifest includes environment variables
|
||||
- **WHEN** the manifest is used for spawning
|
||||
- **THEN** it defines required environment variables
|
||||
- **AND** it defines optional config overrides
|
||||
|
||||
### Requirement: code-server manifest is valid
|
||||
The system SHALL validate the code-server manifest against the tool manifest schema.
|
||||
|
||||
#### Scenario: Schema validation
|
||||
- **WHEN** the manifest is loaded at startup
|
||||
- **THEN** it passes schema validation
|
||||
- **AND** any errors prevent application startup
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool instance status is tracked
|
||||
The system SHALL track the lifecycle status of tool instances.
|
||||
|
||||
#### Scenario: Status transitions
|
||||
- **WHEN** a tool instance is created
|
||||
- **THEN** its status is "creating"
|
||||
- **AND** when the container starts, status becomes "running"
|
||||
- **AND** when stopped, status becomes "stopped"
|
||||
- **AND** on error, status becomes "error"
|
||||
|
||||
#### Scenario: Status polling
|
||||
- **WHEN** the user views a tool instance
|
||||
- **THEN** the frontend polls the status endpoint
|
||||
- **AND** updates the UI when status changes
|
||||
|
||||
### Requirement: Tool instances can be stopped and restarted
|
||||
The system SHALL allow stopping and restarting tool instances.
|
||||
|
||||
#### Scenario: Stop instance
|
||||
- **WHEN** the user clicks "Stop" on a running instance
|
||||
- **THEN** the system stops the Docker container
|
||||
- **AND** updates the status to "stopped"
|
||||
|
||||
#### Scenario: Restart instance
|
||||
- **WHEN** the user clicks "Start" on a stopped instance
|
||||
- **THEN** the system starts the existing container
|
||||
- **AND** updates the status to "running"
|
||||
@@ -0,0 +1,21 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: User can spawn a tool from the UI
|
||||
The system SHALL provide a user interface for spawning tools.
|
||||
|
||||
#### Scenario: Spawn form
|
||||
- **WHEN** the user navigates to /tools/spawn
|
||||
- **THEN** a form is displayed with tool selection
|
||||
- **AND** project selection dropdown
|
||||
- **AND** optional config override fields
|
||||
|
||||
#### Scenario: Tool selection
|
||||
- **WHEN** the user selects a tool from the dropdown
|
||||
- **THEN** the form shows tool-specific configuration options
|
||||
- **AND** a description of the tool
|
||||
|
||||
#### Scenario: Spawn submission
|
||||
- **WHEN** the user submits the spawn form
|
||||
- **THEN** the frontend calls POST /api/v1/tool-instances
|
||||
- **AND** displays a loading state
|
||||
- **AND** redirects to the tool instance detail page on success
|
||||
@@ -0,0 +1,29 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: API can spawn a tool instance
|
||||
The system SHALL provide an endpoint to create and start a tool instance.
|
||||
|
||||
#### Scenario: Spawn code-server
|
||||
- **WHEN** a POST request is made to /api/v1/tool-instances with tool_id and project_id
|
||||
- **THEN** the system creates a ToolInstance record
|
||||
- **AND** generates a Docker Compose service definition
|
||||
- **AND** starts the container via Docker Compose API
|
||||
- **AND** returns the tool instance with status "creating"
|
||||
|
||||
#### Scenario: Spawn with config overrides
|
||||
- **WHEN** a spawn request includes config overrides
|
||||
- **THEN** the overrides are merged with scope-resolved configs
|
||||
- **AND** applied to the container environment
|
||||
|
||||
### Requirement: Spawn validates prerequisites
|
||||
The system SHALL validate prerequisites before spawning.
|
||||
|
||||
#### Scenario: Valid project
|
||||
- **WHEN** the spawn request references a project
|
||||
- **THEN** the project must exist and belong to the user
|
||||
- **AND** the tool definition must exist in the registry
|
||||
|
||||
#### Scenario: Duplicate spawn prevention
|
||||
- **WHEN** a spawn request is made for an already-running instance
|
||||
- **THEN** the system returns the existing instance
|
||||
- **AND** does not create a duplicate container
|
||||
@@ -0,0 +1,58 @@
|
||||
## 1. Manifest Refinement
|
||||
|
||||
- [x] 1.1 Update apps/api/app/tools/manifests/code-server.yml with complete runtime config
|
||||
- [x] 1.2 Add Docker image, ports, volumes, env vars to manifest
|
||||
- [x] 1.3 Validate manifest against ToolManifest schema
|
||||
- [x] 1.4 Test manifest loading at application startup
|
||||
|
||||
## 2. Spawn Service
|
||||
|
||||
- [x] 2.1 Create apps/api/app/services/spawn.py with SpawnService class
|
||||
- [x] 2.2 Implement Docker Compose service generation from manifest
|
||||
- [x] 2.3 Implement container start/stop via Docker Compose API
|
||||
- [x] 2.4 Integrate Traefik label generation (FN-006)
|
||||
- [x] 2.5 Integrate config/secrets runtime injection (FN-009)
|
||||
- [x] 2.6 Implement workspace volume mounting
|
||||
- [x] 2.7 Implement SSH key mounting for Git access
|
||||
- [x] 2.8 Add container status polling
|
||||
|
||||
## 3. Backend API
|
||||
|
||||
- [x] 3.1 Enhance POST /api/v1/tool-instances with spawn logic
|
||||
- [x] 3.2 Add DELETE /api/v1/tool-instances/:id/stop endpoint
|
||||
- [x] 3.3 Add POST /api/v1/tool-instances/:id/start endpoint
|
||||
- [x] 3.4 Add GET /api/v1/tool-instances/:id/status endpoint
|
||||
- [x] 3.5 Add validation for project ownership and tool existence
|
||||
- [x] 3.6 Prevent duplicate spawn of running instances
|
||||
|
||||
## 4. Frontend UI
|
||||
|
||||
- [x] 4.1 Create ToolSpawn page at /tools/spawn
|
||||
- [x] 4.2 Implement tool selection dropdown from registry
|
||||
- [x] 4.3 Implement project selection dropdown
|
||||
- [x] 4.4 Add config override fields based on manifest
|
||||
- [x] 4.5 Create ToolInstanceDetail page at /tools/:id
|
||||
- [x] 4.6 Display instance status, subdomain URL, and controls (stop/start)
|
||||
- [x] 4.7 Add "Open Tool" button that opens subdomain in new tab
|
||||
|
||||
## 5. Auth Integration
|
||||
|
||||
- [x] 5.1 Configure Traefik forwardAuth middleware for code-server
|
||||
- [x] 5.2 Implement auth validation endpoint for Traefik
|
||||
- [x] 5.3 Disable code-server built-in auth (PASSWORD: "")
|
||||
- [x] 5.4 Test that unauthenticated requests are blocked
|
||||
|
||||
## 6. Testing & Verification
|
||||
|
||||
- [x] 6.1 Write backend tests for SpawnService
|
||||
- [x] 6.2 Write backend tests for tool instance lifecycle endpoints
|
||||
- [x] 6.3 Test container spawn in local Docker environment
|
||||
- [x] 6.4 Verify Traefik routing to spawned container
|
||||
- [x] 6.5 Run full test suite: `make test`
|
||||
- [x] 6.6 Run linters: `make lint`
|
||||
|
||||
## 7. Documentation
|
||||
|
||||
- [x] 7.1 Update docs/development.md with spawn workflow
|
||||
- [x] 7.2 Add code-server setup guide to docs/architecture.md
|
||||
- [x] 7.3 Document auth proxy configuration
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-14
|
||||
@@ -0,0 +1,75 @@
|
||||
## Context
|
||||
|
||||
The frontend is currently a static scaffold (FN-002) with no routing, auth, or API integration. The backend has complete domain models, CRUD routers, and auth dependencies (FN-004, FN-011). This design bridges the gap by establishing the frontend architecture needed for all user-facing features.
|
||||
|
||||
Current frontend state:
|
||||
- Single App.tsx with static HTML
|
||||
- No routing, state management, or API client
|
||||
- No auth integration
|
||||
- Tests only verify static rendering
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Deliver a functional auth flow (login/logout via Authentik OIDC)
|
||||
- Provide a responsive dashboard shell with navigation
|
||||
- Enable project CRUD operations from the UI
|
||||
- Establish typed API client patterns
|
||||
- Set up environment-based configuration
|
||||
|
||||
**Non-Goals:**
|
||||
- Full tool spawn UI (deferred to FN-010/FN-008)
|
||||
- Config/secrets management UI (deferred to FN-009)
|
||||
- Repository connection UI (deferred to future task)
|
||||
- Real-time updates or WebSockets
|
||||
- Mobile-optimized responsive design (basic responsiveness only)
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. React Router v7 for routing**
|
||||
- Rationale: Industry standard, integrates well with React 19, supports nested routes and loaders
|
||||
- Alternative: TanStack Router - more type-safe but steeper learning curve, overkill for MVP
|
||||
|
||||
**2. TanStack Query (React Query) for server state**
|
||||
- Rationale: Standard for API caching, background refetching, and optimistic updates
|
||||
- Alternative: SWR - similar but TanStack Query has better TypeScript support and devtools
|
||||
|
||||
**3. Zustand for client state**
|
||||
- Rationale: Lightweight, TypeScript-friendly, minimal boilerplate vs Redux
|
||||
- Alternative: Context API - sufficient for auth but Zustand scales better for future features
|
||||
|
||||
**4. HTTP client: fetch API with thin wrapper**
|
||||
- Rationale: No extra dependency needed, native fetch is sufficient
|
||||
- Alternative: Axios - adds bundle size, fetch handles our use cases
|
||||
|
||||
**5. Auth: OIDC Authorization Code flow with PKCE**
|
||||
- Rationale: Secure, recommended by OAuth 2.1, Authentik supports it
|
||||
- Implementation: redirect to Authentik authorize endpoint, callback handles code exchange
|
||||
|
||||
**6. Component library: Headless UI + Tailwind CSS**
|
||||
- Rationale: Unstyled primitives give full control, Tailwind is already in Vite scaffold
|
||||
- Alternative: Material UI - opinionated, harder to customize
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Auth token storage in browser**
|
||||
→ Mitigation: Use httpOnly cookies (set by backend callback) or secure storage. Never localStorage. Implement CSRF protection.
|
||||
|
||||
**[Risk] OIDC library bundle size**
|
||||
→ Mitigation: Use lightweight oauth4webapi or implement PKCE manually (~2KB vs 50KB+ for oidc-client-ts)
|
||||
|
||||
**[Risk] CORS complexity between web and API**
|
||||
→ Mitigation: Configure CORS in FastAPI to allow web origin. Use same-origin deployment in production (Traefik routes both).
|
||||
|
||||
**[Risk] Test complexity with auth flows**
|
||||
→ Mitigation: Mock auth context in tests, test components in isolation. E2E tests deferred post-MVP.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration needed - this is additive to the scaffold.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we use a pre-built OIDC client library or implement PKCE manually?
|
||||
2. Do we need refresh token rotation or are short-lived access tokens sufficient?
|
||||
3. Should the API client auto-retry on 401 or redirect immediately?
|
||||
@@ -0,0 +1,32 @@
|
||||
## Why
|
||||
|
||||
The backend API is fully scaffolded with domain models, routers, and authentication dependencies, but the frontend remains a static scaffold page (FN-002). Users cannot sign in, view projects, or interact with any backend functionality. This change delivers the foundational frontend architecture needed to unlock all user-facing MVP features.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Authentik OIDC integration**: Auth provider with login/logout flow, token management, and automatic user provisioning
|
||||
- **Dashboard shell**: Responsive layout with header, navigation sidebar, and main content area
|
||||
- **Navigation routes**: Dashboard, Projects, Repositories, Tools, Settings pages with React Router
|
||||
- **Typed API client**: Generated or hand-written client for all `/api/v1/*` endpoints
|
||||
- **Project list UI**: Display user's projects with create/edit capabilities
|
||||
- **Environment config layer**: Vite env var integration for API URL, auth endpoints
|
||||
- **Auth-guarded routes**: Redirect unauthenticated users to login
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `auth-oidc`: Authentik OIDC authentication flow, token storage, session management
|
||||
- `dashboard-shell`: Responsive layout with navigation, header, and content area
|
||||
- `project-management-ui`: Project list, create, edit, delete views
|
||||
- `api-client`: Typed HTTP client for backend API consumption
|
||||
- `route-guards`: Authentication-based route protection and redirects
|
||||
|
||||
### Modified Capabilities
|
||||
- None (this is purely additive to the existing scaffold)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/web/src/**: All new frontend code
|
||||
- **apps/web/package.json**: New dependencies (react-router-dom, @tanstack/react-query, etc.)
|
||||
- **apps/api/app/auth/dependencies.py**: CORS and auth flow alignment
|
||||
- **docs/development.md**: Updated frontend development instructions
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: API client handles all backend endpoints
|
||||
The system SHALL provide a typed HTTP client for all backend API endpoints.
|
||||
|
||||
#### Scenario: GET request
|
||||
- **WHEN** the client calls api.get('/projects')
|
||||
- **THEN** it sends a GET request to /api/v1/projects
|
||||
- **AND** returns typed Project[] data
|
||||
- **AND** includes the Authorization header with the current access token
|
||||
|
||||
#### Scenario: POST request
|
||||
- **WHEN** the client calls api.post('/projects', data)
|
||||
- **THEN** it sends a POST request with JSON body
|
||||
- **AND** returns typed Project data
|
||||
|
||||
#### Scenario: Error handling
|
||||
- **WHEN** a request returns 4xx or 5xx
|
||||
- **THEN** the client throws an ApiError with status code and message
|
||||
- **AND** the error can be caught and displayed to the user
|
||||
|
||||
### Requirement: API client supports request/response types
|
||||
The system SHALL use TypeScript interfaces matching the backend Pydantic schemas.
|
||||
|
||||
#### Scenario: Type safety
|
||||
- **WHEN** a developer uses the API client
|
||||
- **THEN** request and response types are checked at compile time
|
||||
- **AND** mismatches produce TypeScript errors
|
||||
@@ -0,0 +1,36 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: User can authenticate via Authentik OIDC
|
||||
The system SHALL provide an authentication flow using Authentik as the OIDC provider.
|
||||
|
||||
#### Scenario: Successful login
|
||||
- **WHEN** an unauthenticated user clicks "Sign In"
|
||||
- **THEN** the system redirects to Authentik's authorization endpoint with PKCE parameters
|
||||
- **AND** after successful authentication, Authentik redirects back with an authorization code
|
||||
- **AND** the system exchanges the code for tokens
|
||||
- **AND** the user is redirected to the dashboard
|
||||
|
||||
#### Scenario: Automatic user provisioning
|
||||
- **WHEN** a user authenticates for the first time
|
||||
- **THEN** the backend creates a User record automatically
|
||||
- **AND** the user can access their projects immediately
|
||||
|
||||
#### Scenario: Logout
|
||||
- **WHEN** an authenticated user clicks "Sign Out"
|
||||
- **THEN** the system clears all session data
|
||||
- **AND** redirects to Authentik's end_session_endpoint
|
||||
- **AND** the user is redirected back to the login page
|
||||
|
||||
### Requirement: Auth state is managed globally
|
||||
The system SHALL maintain authentication state accessible throughout the application.
|
||||
|
||||
#### Scenario: Auth context available
|
||||
- **WHEN** the application loads
|
||||
- **THEN** an auth context provider wraps the component tree
|
||||
- **AND** child components can read the current auth state (loading, authenticated, unauthenticated, error)
|
||||
|
||||
#### Scenario: Token refresh
|
||||
- **WHEN** an API request returns 401 due to expired token
|
||||
- **THEN** the system attempts token refresh
|
||||
- **AND** retries the original request with the new token
|
||||
- **AND** if refresh fails, redirects to login
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dashboard provides responsive layout
|
||||
The system SHALL provide a consistent layout with header, navigation, and content area.
|
||||
|
||||
#### Scenario: Layout structure
|
||||
- **WHEN** the user views any authenticated page
|
||||
- **THEN** a header displays the application name and user avatar
|
||||
- **AND** a sidebar shows navigation links (Dashboard, Projects, Repositories, Tools, Settings)
|
||||
- **AND** the main content area renders the current route's component
|
||||
|
||||
#### Scenario: Collapsible sidebar
|
||||
- **WHEN** the user is on a mobile device
|
||||
- **THEN** the sidebar is initially collapsed
|
||||
- **AND** a hamburger menu toggles the sidebar visibility
|
||||
|
||||
### Requirement: Navigation reflects auth state
|
||||
The system SHALL show/hide navigation items based on authentication status.
|
||||
|
||||
#### Scenario: Authenticated navigation
|
||||
- **WHEN** the user is authenticated
|
||||
- **THEN** all navigation links are visible
|
||||
- **AND** "Sign Out" is available in the user menu
|
||||
|
||||
#### Scenario: Unauthenticated navigation
|
||||
- **WHEN** the user is not authenticated
|
||||
- **THEN** only "Sign In" is shown
|
||||
- **AND** accessing protected routes redirects to login
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: User can view their projects
|
||||
The system SHALL display a list of projects belonging to the authenticated user.
|
||||
|
||||
#### Scenario: Project list page
|
||||
- **WHEN** the user navigates to /projects
|
||||
- **THEN** the system fetches projects from /api/v1/projects
|
||||
- **AND** displays each project with name, description, and created date
|
||||
- **AND** shows an empty state when no projects exist
|
||||
|
||||
#### Scenario: Project detail
|
||||
- **WHEN** the user clicks on a project
|
||||
- **THEN** the system navigates to /projects/:id
|
||||
- **AND** displays project details including repositories and tool instances
|
||||
|
||||
### Requirement: User can create a project
|
||||
The system SHALL allow authenticated users to create new projects.
|
||||
|
||||
#### Scenario: Create project form
|
||||
- **WHEN** the user clicks "New Project"
|
||||
- **THEN** a form appears with name and description fields
|
||||
- **AND** the name field validates for non-empty and URL-friendly slug generation
|
||||
- **AND** submitting the form POSTs to /api/v1/projects
|
||||
- **AND** on success, the user is redirected to the new project
|
||||
|
||||
### Requirement: User can edit and delete projects
|
||||
The system SHALL allow project owners to modify or remove their projects.
|
||||
|
||||
#### Scenario: Edit project
|
||||
- **WHEN** the user clicks "Edit" on a project
|
||||
- **THEN** a pre-filled form appears
|
||||
- **AND** submitting updates the project via PUT /api/v1/projects/:id
|
||||
|
||||
#### Scenario: Delete project
|
||||
- **WHEN** the user clicks "Delete" on a project
|
||||
- **THEN** a confirmation dialog appears
|
||||
- **AND** confirming sends DELETE /api/v1/projects/:id
|
||||
- **AND** the project is removed from the list
|
||||
@@ -0,0 +1,24 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Protected routes require authentication
|
||||
The system SHALL prevent unauthenticated users from accessing protected pages.
|
||||
|
||||
#### Scenario: Unauthenticated access attempt
|
||||
- **WHEN** an unauthenticated user navigates to /projects
|
||||
- **THEN** the system redirects to /login
|
||||
- **AND** stores the intended destination for post-login redirect
|
||||
|
||||
#### Scenario: Authenticated access
|
||||
- **WHEN** an authenticated user navigates to /projects
|
||||
- **THEN** the route renders normally
|
||||
|
||||
### Requirement: Public routes are accessible
|
||||
The system SHALL allow unauthenticated access to public pages.
|
||||
|
||||
#### Scenario: Login page
|
||||
- **WHEN** an unauthenticated user navigates to /login
|
||||
- **THEN** the login page renders without redirect
|
||||
|
||||
#### Scenario: Health/status pages
|
||||
- **WHEN** any user navigates to /health
|
||||
- **THEN** the page renders without authentication
|
||||
@@ -0,0 +1,72 @@
|
||||
## 1. Setup & Dependencies
|
||||
|
||||
- [x] 1.1 Install frontend dependencies: react-router-dom, @tanstack/react-query, zustand, @headlessui/react
|
||||
- [x] 1.2 Set up Tailwind CSS configuration (tailwind.config.js, postcss.config.js)
|
||||
- [x] 1.3 Create environment type definitions in apps/web/src/env.d.ts
|
||||
- [x] 1.4 Add Vite environment variables to .env.example (VITE_API_URL, VITE_OIDC_ISSUER, etc.)
|
||||
|
||||
## 2. API Client & Types
|
||||
|
||||
- [x] 2.1 Create apps/web/src/api/client.ts with typed fetch wrapper and auth header injection
|
||||
- [x] 2.2 Generate or create TypeScript interfaces matching backend schemas (Project, User, etc.)
|
||||
- [x] 2.3 Implement error handling with ApiError class
|
||||
- [x] 2.4 Add request/response logging in debug mode
|
||||
|
||||
## 3. Authentication
|
||||
|
||||
- [x] 3.1 Create apps/web/src/auth/oidc.ts with PKCE code generation and token exchange
|
||||
- [x] 3.2 Implement auth store (Zustand) with state: loading, authenticated, unauthenticated, error
|
||||
- [x] 3.3 Create AuthProvider component wrapping the app
|
||||
- [x] 3.4 Implement login redirect to Authentik authorize endpoint
|
||||
- [x] 3.5 Implement callback handler (/callback route) for code exchange
|
||||
- [x] 3.6 Implement logout with end_session_endpoint redirect
|
||||
- [x] 3.7 Add token refresh logic for expired access tokens
|
||||
|
||||
## 4. Routing & Layout
|
||||
|
||||
- [x] 4.1 Set up React Router with route definitions in apps/web/src/router.tsx
|
||||
- [x] 4.2 Create DashboardLayout component with header, sidebar, and outlet
|
||||
- [x] 4.3 Implement RouteGuard component for protected routes
|
||||
- [x] 4.4 Add public routes: /login, /callback
|
||||
- [x] 4.5 Add protected routes: /, /projects, /projects/:id, /tools, /settings
|
||||
|
||||
## 5. Dashboard Shell
|
||||
|
||||
- [x] 5.1 Create Header component with app name and user avatar dropdown
|
||||
- [x] 5.2 Create Sidebar component with navigation links
|
||||
- [x] 5.3 Implement mobile-responsive sidebar toggle
|
||||
- [x] 5.4 Add active route highlighting in sidebar
|
||||
- [x] 5.5 Create Dashboard home page with welcome content
|
||||
|
||||
## 6. Project Management UI
|
||||
|
||||
- [x] 6.1 Create ProjectList page fetching from /api/v1/projects
|
||||
- [x] 6.2 Implement ProjectCard component for list view
|
||||
- [x] 6.3 Add empty state when no projects exist
|
||||
- [x] 6.4 Create ProjectDetail page at /projects/:id
|
||||
- [x] 6.5 Implement NewProject form with validation (name, description)
|
||||
- [x] 6.6 Implement EditProject form with pre-filled data
|
||||
- [x] 6.7 Add delete confirmation dialog for projects
|
||||
- [x] 6.8 Wire up TanStack Query mutations for create/update/delete
|
||||
|
||||
## 7. Placeholder Pages
|
||||
|
||||
- [x] 7.1 Create Tools page placeholder
|
||||
- [x] 7.2 Create Settings page placeholder
|
||||
- [x] 7.3 Create Repositories page placeholder
|
||||
|
||||
## 8. Testing & Verification
|
||||
|
||||
- [x] 8.1 Write unit tests for auth store
|
||||
- [x] 8.2 Write unit tests for API client error handling
|
||||
- [x] 8.3 Write tests for RouteGuard component
|
||||
- [x] 8.4 Update App.test.tsx to test routing
|
||||
- [x] 8.5 Run `pnpm test` and fix any failures
|
||||
- [x] 8.6 Run `pnpm lint` and fix any issues
|
||||
- [x] 8.7 Run `pnpm typecheck` and fix any errors
|
||||
|
||||
## 9. Documentation
|
||||
|
||||
- [x] 9.1 Update docs/development.md with frontend auth setup instructions
|
||||
- [x] 9.2 Update README.md with new environment variables
|
||||
- [x] 9.3 Add frontend architecture notes to docs/architecture.md
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-14
|
||||
@@ -0,0 +1,63 @@
|
||||
## Context
|
||||
|
||||
The backend has Config and Secret models (FN-004) with scope fields, but no frontend UI or runtime injection. SSH keys already use Fernet encryption (FN-011), so the encryption pattern is established. This design completes the config/secrets lifecycle.
|
||||
|
||||
Current state:
|
||||
- Config model: key, value, scope (global/user/project/instance), scope_id
|
||||
- Secret model: key, encrypted_value, scope, scope_id
|
||||
- Fernet encryption utilities exist in app/encryption.py
|
||||
- No UI for management
|
||||
- No runtime injection into containers
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Allow users to manage configs and secrets via UI
|
||||
- Inject configs/secrets into tool containers at spawn time
|
||||
- Support scope-based inheritance (instance overrides project overrides user overrides global)
|
||||
- Maintain encryption for all secret values
|
||||
|
||||
**Non-Goals:**
|
||||
- Secret versioning or history
|
||||
- Automatic secret rotation
|
||||
- Integration with external secret managers (Vault, AWS Secrets Manager)
|
||||
- Config/secrets for non-tool resources
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Mount configs as files, secrets as env vars**
|
||||
- Rationale: Configs (JSON) are often files (e.g., settings.json). Secrets are typically env vars.
|
||||
- Config mount: `/app/config/<key>.json`
|
||||
- Secret env: `<KEY>=<decrypted_value>`
|
||||
|
||||
**2. Scope resolution: closest match wins**
|
||||
- Rationale: Instance-specific values should override project defaults
|
||||
- Resolution order: instance → project → user → global
|
||||
|
||||
**3. Secret values never sent to frontend decrypted**
|
||||
- Rationale: Security. Frontend only sees masked values (e.g., `••••••`).
|
||||
- Decryption happens only in backend during runtime injection
|
||||
|
||||
**4. Config values are plaintext (not encrypted)**
|
||||
- Rationale: Configs are not sensitive. Encrypting them adds complexity without security benefit.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Secret injection at spawn time could fail silently**
|
||||
→ Mitigation: Validate all referenced secrets exist before spawning. Return error if missing.
|
||||
|
||||
**[Risk] Config files in containers could be read by other processes**
|
||||
→ Mitigation: Mount config files with restrictive permissions (0400). Run containers as non-root.
|
||||
|
||||
**[Risk] Large configs could exceed container env var limits**
|
||||
→ Mitigation: Document size limits. Consider config file mounting for large values.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration needed. This extends existing models.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should configs support JSON schema validation?
|
||||
2. Do we need bulk import/export for configs/secrets?
|
||||
3. Should secret keys be validated against a naming convention?
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
Tool instances need runtime configuration and secrets (API keys, database passwords, etc.). The backend has Config and Secret models (FN-004), but there's no UI for users to manage these values, and no runtime injection mechanism to pass them into spawned containers.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Config management UI**: Frontend pages for creating, updating, and deleting config values at global/user/project/instance scopes
|
||||
- **Secret management UI**: Frontend pages for encrypted secret storage with masked value display
|
||||
- **Runtime injection**: Backend service that mounts configs and secrets into tool containers at spawn time
|
||||
- **Scope-based access control**: Configs/secrets respect scope hierarchy (global → user → project → instance)
|
||||
- **Encryption verification**: Ensure Fernet encryption is properly applied to all secret values
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `config-management`: CRUD operations for configuration values with scope support
|
||||
- `secret-management`: Encrypted storage and retrieval of sensitive values
|
||||
- `runtime-injection`: Mount configs and secrets into tool containers at spawn
|
||||
|
||||
### Modified Capabilities
|
||||
- None (extends existing Config/Secret models)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/web/src/**: New config and secret management pages
|
||||
- **apps/api/app/routers/configs.py**: Enhanced with scope filtering
|
||||
- **apps/api/app/routers/secrets.py**: Enhanced with scope filtering
|
||||
- **apps/api/app/services/**: New runtime injection service
|
||||
- **apps/api/app/models/**: Potential Config/Secret model updates for scope validation
|
||||
@@ -0,0 +1,37 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: User can create config values
|
||||
The system SHALL allow users to create configuration values at various scopes.
|
||||
|
||||
#### Scenario: Create project config
|
||||
- **WHEN** the user navigates to project settings
|
||||
- **AND** clicks "Add Config"
|
||||
- **THEN** a form appears with key, value, and scope fields
|
||||
- **AND** submitting creates a config at the selected scope
|
||||
|
||||
#### Scenario: Config scope validation
|
||||
- **WHEN** the user creates a config
|
||||
- **THEN** the scope must be one of: global, user, project, instance
|
||||
- **AND** the scope_id must match the selected scope type
|
||||
|
||||
### Requirement: User can view and update configs
|
||||
The system SHALL display configs with scope-based filtering.
|
||||
|
||||
#### Scenario: List configs
|
||||
- **WHEN** the user views configs for a project
|
||||
- **THEN** all configs visible at project scope or above are displayed
|
||||
- **AND** values are shown as formatted JSON
|
||||
|
||||
#### Scenario: Update config
|
||||
- **WHEN** the user edits a config value
|
||||
- **THEN** the updated value is saved
|
||||
- **AND** the change takes effect on next tool spawn
|
||||
|
||||
### Requirement: User can delete configs
|
||||
The system SHALL allow deletion of config values.
|
||||
|
||||
#### Scenario: Delete config
|
||||
- **WHEN** the user clicks delete on a config
|
||||
- **THEN** a confirmation dialog appears
|
||||
- **AND** confirming removes the config
|
||||
- **AND** the config is no longer injected into containers
|
||||
@@ -0,0 +1,40 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Configs are mounted into tool containers
|
||||
The system SHALL mount configuration values as files into spawned tool containers.
|
||||
|
||||
#### Scenario: Config file mount
|
||||
- **WHEN** a tool instance is spawned
|
||||
- **THEN** all applicable configs are written to /app/config/
|
||||
- **AND** each config is a separate JSON file named by key
|
||||
- **AND** files have restrictive permissions (0400)
|
||||
|
||||
#### Scenario: Config scope resolution
|
||||
- **WHEN** configs are resolved for a tool instance
|
||||
- **THEN** the system collects configs from all applicable scopes
|
||||
- **AND** instance scope overrides project scope
|
||||
- **AND** project scope overrides user scope
|
||||
- **AND** user scope overrides global scope
|
||||
|
||||
### Requirement: Secrets are injected as environment variables
|
||||
The system SHALL inject secret values as environment variables into tool containers.
|
||||
|
||||
#### Scenario: Secret env var injection
|
||||
- **WHEN** a tool instance is spawned
|
||||
- **THEN** all applicable secrets are decrypted
|
||||
- **AND** injected as environment variables with uppercase keys
|
||||
- **AND** the container process can access them
|
||||
|
||||
#### Scenario: Secret scope resolution
|
||||
- **WHEN** secrets are resolved for a tool instance
|
||||
- **THEN** the same scope hierarchy applies as configs
|
||||
- **AND** closest scope wins on key collision
|
||||
|
||||
### Requirement: Missing secrets fail spawn
|
||||
The system SHALL prevent spawning if referenced secrets are missing.
|
||||
|
||||
#### Scenario: Validate secrets before spawn
|
||||
- **WHEN** a spawn request references a secret by key
|
||||
- **AND** the secret does not exist in any applicable scope
|
||||
- **THEN** the spawn fails with a clear error message
|
||||
- **AND** no container is created
|
||||
@@ -0,0 +1,37 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: User can create secrets
|
||||
The system SHALL allow users to store encrypted secret values.
|
||||
|
||||
#### Scenario: Create secret
|
||||
- **WHEN** the user navigates to project secrets
|
||||
- **AND** clicks "Add Secret"
|
||||
- **THEN** a form appears with key and value fields
|
||||
- **AND** the value is encrypted with Fernet before storage
|
||||
- **AND** the user sees a masked value (e.g., ••••••) after creation
|
||||
|
||||
#### Scenario: Secret scope
|
||||
- **WHEN** the user creates a secret
|
||||
- **THEN** the scope can be user, project, or instance
|
||||
- **AND** the secret is only visible within that scope hierarchy
|
||||
|
||||
### Requirement: Secrets are never exposed decrypted
|
||||
The system SHALL prevent decrypted secret values from being sent to the frontend.
|
||||
|
||||
#### Scenario: Secret list display
|
||||
- **WHEN** the user views the secrets list
|
||||
- **THEN** only secret keys and scopes are visible
|
||||
- **AND** values are always masked
|
||||
|
||||
#### Scenario: Secret update
|
||||
- **WHEN** the user updates a secret
|
||||
- **THEN** only the new value is sent to the backend
|
||||
- **AND** the old value is replaced (not displayed)
|
||||
|
||||
### Requirement: User can delete secrets
|
||||
The system SHALL allow deletion of secret values.
|
||||
|
||||
#### Scenario: Delete secret
|
||||
- **WHEN** the user deletes a secret
|
||||
- **THEN** the encrypted value is permanently removed
|
||||
- **AND** the secret is no longer injected into containers
|
||||
@@ -0,0 +1,48 @@
|
||||
## 1. Backend Enhancements
|
||||
|
||||
- [ ] 1.1 Update Config model with scope validation methods
|
||||
- [ ] 1.2 Update Secret model with encryption verification
|
||||
- [ ] 1.3 Enhance configs router with scope filtering and hierarchy resolution
|
||||
- [ ] 1.4 Enhance secrets router with scope filtering and hierarchy resolution
|
||||
- [ ] 1.5 Create apps/api/app/services/runtime_injection.py for config/secret resolution
|
||||
- [ ] 1.6 Implement config file generation for container mounts
|
||||
- [ ] 1.7 Implement secret env var generation for container injection
|
||||
- [ ] 1.8 Add validation to fail spawn when referenced secrets are missing
|
||||
|
||||
## 2. Frontend - Config Management
|
||||
|
||||
- [ ] 2.1 Create ConfigList component at /projects/:id/configs
|
||||
- [ ] 2.2 Implement ConfigForm for creating/updating configs
|
||||
- [ ] 2.3 Add scope selector (project/instance/global) to config form
|
||||
- [ ] 2.4 Implement config delete with confirmation
|
||||
- [ ] 2.5 Add JSON formatting for config values
|
||||
|
||||
## 3. Frontend - Secret Management
|
||||
|
||||
- [ ] 3.1 Create SecretList component at /projects/:id/secrets
|
||||
- [ ] 3.2 Implement SecretForm for creating/updating secrets
|
||||
- [ ] 3.3 Add masked value display (never show decrypted)
|
||||
- [ ] 3.4 Implement secret delete with confirmation
|
||||
- [ ] 3.5 Add scope selector to secret form
|
||||
|
||||
## 4. Runtime Integration
|
||||
|
||||
- [ ] 4.1 Integrate runtime injection into tool instance spawn endpoint
|
||||
- [ ] 4.2 Update Docker Compose generation to include config mounts
|
||||
- [ ] 4.3 Update Docker Compose generation to include secret env vars
|
||||
- [ ] 4.4 Test config/secret injection in local Docker environment
|
||||
|
||||
## 5. Testing & Verification
|
||||
|
||||
- [ ] 5.1 Write backend tests for config scope resolution
|
||||
- [ ] 5.2 Write backend tests for secret encryption/decryption
|
||||
- [ ] 5.3 Write backend tests for runtime injection
|
||||
- [ ] 5.4 Write frontend tests for ConfigList and SecretList
|
||||
- [ ] 5.5 Run full test suite: `make test`
|
||||
- [ ] 5.6 Run linters: `make lint`
|
||||
|
||||
## 6. Documentation
|
||||
|
||||
- [ ] 6.1 Update docs/development.md with config/secrets workflow
|
||||
- [ ] 6.2 Add config/secrets UI guide to docs/architecture.md
|
||||
- [ ] 6.3 Document scope hierarchy and resolution rules
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-14
|
||||
@@ -0,0 +1,67 @@
|
||||
## Context
|
||||
|
||||
The platform routes tool instances via Traefik using subdomain patterns like `https://{tool}-{project}-{user}.{tool_domain}`. Currently, there's no automated label generation or production deployment configuration. This design establishes the deployment architecture.
|
||||
|
||||
Current state:
|
||||
- `docker-compose.yml` for local dev only
|
||||
- `docker-compose.traefik.yml` exists but is minimal
|
||||
- `deploy/` directory has skeleton files
|
||||
- No automated Traefik label generation
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Generate Traefik labels automatically when spawning tools
|
||||
- Provide production-ready Docker Compose stack
|
||||
- Support Portainer-managed deployment
|
||||
- Enable HTTPS with automatic certificate management
|
||||
|
||||
**Non-Goals:**
|
||||
- Kubernetes deployment (deferred post-MVP)
|
||||
- Multi-region or high-availability setup
|
||||
- Custom reverse proxy (Traefik is the only supported option)
|
||||
- Automatic DNS management
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Label generation in backend, not in Docker Compose**
|
||||
- Rationale: Backend has all metadata (user slug, project slug, tool ID). Generating labels at spawn time is more flexible than static Compose files.
|
||||
- Implementation: `TraefikLabelGenerator` service class
|
||||
|
||||
**2. Subdomain pattern: `{tool}-{project}-{user}.{domain}`**
|
||||
- Rationale: Unique, deterministic, human-readable
|
||||
- Example: `code-server-myapp-alice.headquarter.example.com`
|
||||
|
||||
**3. Separate Docker networks: `platform` and `tools`**
|
||||
- Rationale: Network isolation between platform services and user tools
|
||||
- Platform network: API, web, Traefik, database
|
||||
- Tools network: Traefik + tool containers only
|
||||
|
||||
**4. Portainer as the deployment target**
|
||||
- Rationale: Docker Compose-native, web UI for operators, supports stacks and webhooks
|
||||
- Alternative: Raw Docker Compose on VM - less operator-friendly
|
||||
|
||||
**5. Let's Encrypt for HTTPS in production**
|
||||
- Rationale: Free, automatic, Traefik has built-in support
|
||||
- Alternative: Custom certificates - adds operational burden
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] Traefik label complexity grows with features**
|
||||
→ Mitigation: Keep label generation centralized in one service class. Test label output against Traefik schema.
|
||||
|
||||
**[Risk] Portainer stack updates require downtime**
|
||||
→ Mitigation: Use rolling updates where possible. Document blue-green deployment strategy.
|
||||
|
||||
**[Risk] Subdomain collision**
|
||||
→ Mitigation: Enforce unique project slugs per user. Include user slug in subdomain.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration - new deployment stack is additive.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should we support custom domains per user/project in MVP?
|
||||
2. Do we need basic auth or IP allow-listing for Traefik dashboard?
|
||||
3. Should tool containers run on a separate Docker daemon for security?
|
||||
@@ -0,0 +1,31 @@
|
||||
## Why
|
||||
|
||||
The scaffold provides local Docker Compose development (FN-002) but lacks production deployment configuration. Without Traefik label generation and production stacks, tool instances cannot receive HTTPS subdomains, blocking the core value proposition of the platform.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Traefik label generator**: Backend service that generates Docker labels for subdomain routing based on tool instance metadata
|
||||
- **Production Docker Compose stack**: `docker-compose.prod.yml` with API, web, Traefik, and PostgreSQL services
|
||||
- **Portainer stack definition**: Docker Compose file optimized for Portainer deployment
|
||||
- **Dynamic subdomain routing**: Automatic Traefik rule generation for spawned tool containers
|
||||
- **HTTPS configuration**: Let's Encrypt or custom certificate support via Traefik
|
||||
- **Network isolation**: Separate Docker networks for platform and tool containers
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `traefik-label-generator`: Generate Traefik Docker labels for tool subdomain routing
|
||||
- `production-compose-stack`: Production Docker Compose configuration
|
||||
- `portainer-deployment`: Portainer-friendly stack definition and deployment guide
|
||||
- `subdomain-routing`: Dynamic HTTPS subdomain allocation for tool instances
|
||||
|
||||
### Modified Capabilities
|
||||
- None (this extends the existing deployment skeleton)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/api/app/services/**: New Traefik label generation service
|
||||
- **apps/api/app/routers/tool_instances.py**: Integrate label generation on spawn
|
||||
- **deploy/**: New production deployment files
|
||||
- **docker-compose.prod.yml**: Production stack definition
|
||||
- **docs/deployment.md**: Updated deployment instructions
|
||||
@@ -0,0 +1,22 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Stack deploys via Portainer
|
||||
The system SHALL provide a Portainer-compatible stack definition.
|
||||
|
||||
#### Scenario: Portainer stack file
|
||||
- **WHEN** an operator deploys via Portainer
|
||||
- **THEN** they can paste the stack definition into Portainer's stack editor
|
||||
- **AND** Portainer can pull and deploy all services
|
||||
|
||||
#### Scenario: Environment variables in Portainer
|
||||
- **WHEN** the stack is deployed via Portainer
|
||||
- **THEN** environment variables are configured in Portainer's UI
|
||||
- **AND** the stack references these variables
|
||||
|
||||
### Requirement: Deployment documentation is complete
|
||||
The system SHALL provide operator documentation for deployment.
|
||||
|
||||
#### Scenario: Deployment guide
|
||||
- **WHEN** an operator reads docs/deployment.md
|
||||
- **THEN** they find step-by-step instructions for Portainer deployment
|
||||
- **AND** prerequisites and assumptions are clearly stated
|
||||
@@ -0,0 +1,28 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Production stack includes all required services
|
||||
The system SHALL provide a production Docker Compose stack with API, web, Traefik, and PostgreSQL.
|
||||
|
||||
#### Scenario: Stack services
|
||||
- **WHEN** the production stack is deployed
|
||||
- **THEN** the following services run: api, web, traefik, db
|
||||
- **AND** Traefik routes requests to the appropriate service
|
||||
- **AND** services communicate via isolated Docker networks
|
||||
|
||||
#### Scenario: Environment configuration
|
||||
- **WHEN** the stack starts
|
||||
- **THEN** it reads environment variables from .env
|
||||
- **AND** sensitive values are not hardcoded
|
||||
|
||||
### Requirement: Production stack is secure by default
|
||||
The system SHALL configure security headers and access controls in production.
|
||||
|
||||
#### Scenario: HTTPS only
|
||||
- **WHEN** the stack runs in production
|
||||
- **THEN** all traffic uses HTTPS
|
||||
- **AND** HTTP redirects to HTTPS
|
||||
|
||||
#### Scenario: Network isolation
|
||||
- **WHEN** the stack is deployed
|
||||
- **THEN** platform services and tool containers are on separate networks
|
||||
- **AND** tool containers cannot access the database directly
|
||||
@@ -0,0 +1,23 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Each tool instance gets a unique subdomain
|
||||
The system SHALL assign a unique HTTPS subdomain to each running tool instance.
|
||||
|
||||
#### Scenario: Subdomain pattern
|
||||
- **WHEN** a tool instance is spawned
|
||||
- **THEN** its subdomain follows `{tool}-{project}-{user}.{domain}`
|
||||
- **AND** the subdomain is deterministic based on instance metadata
|
||||
|
||||
#### Scenario: Subdomain accessibility
|
||||
- **WHEN** a tool instance reaches running status
|
||||
- **THEN** its subdomain resolves via DNS
|
||||
- **AND** Traefik routes the subdomain to the container
|
||||
- **AND** the user can access the tool via the subdomain URL
|
||||
|
||||
### Requirement: Subdomain is released on stop
|
||||
The system SHALL remove Traefik routing when a tool instance stops.
|
||||
|
||||
#### Scenario: Stop removes routing
|
||||
- **WHEN** a tool instance is stopped
|
||||
- **THEN** Traefik labels are removed or disabled
|
||||
- **AND** the subdomain no longer routes to the container
|
||||
@@ -0,0 +1,24 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool spawn generates Traefik labels
|
||||
The system SHALL generate Docker labels for Traefik when spawning a tool instance.
|
||||
|
||||
#### Scenario: Label generation on spawn
|
||||
- **WHEN** a tool instance is spawned
|
||||
- **THEN** the backend generates Traefik router and service labels
|
||||
- **AND** labels include rule, service, port, and TLS configuration
|
||||
- **AND** labels are stored with the tool instance metadata
|
||||
|
||||
#### Scenario: Label format
|
||||
- **WHEN** labels are generated for a tool instance
|
||||
- **THEN** router rule uses Host(`{subdomain}.{domain}`)
|
||||
- **AND** service points to the container's exposed port
|
||||
- **AND** TLS is enabled with certResolver
|
||||
|
||||
### Requirement: Label generation handles multiple instances
|
||||
The system SHALL generate unique labels for each tool instance.
|
||||
|
||||
#### Scenario: Unique router names
|
||||
- **WHEN** multiple instances of the same tool exist
|
||||
- **THEN** each instance gets a unique router name
|
||||
- **AND** no label collisions occur
|
||||
@@ -0,0 +1,44 @@
|
||||
## 1. Traefik Label Generator
|
||||
|
||||
- [x] 1.1 Create apps/api/app/services/traefik.py with TraefikLabelGenerator class
|
||||
- [x] 1.2 Implement subdomain generation from tool_id, project_slug, user_slug
|
||||
- [x] 1.3 Generate router labels (rule, service, tls)
|
||||
- [x] 1.4 Generate service labels (loadBalancer, port)
|
||||
- [x] 1.5 Add middleware labels for security headers
|
||||
- [x] 1.6 Write unit tests for label generation
|
||||
|
||||
## 2. Backend Integration
|
||||
|
||||
- [x] 2.1 Integrate label generation into tool instance spawn endpoint
|
||||
- [x] 2.2 Store generated labels in tool_instance metadata
|
||||
- [x] 2.3 Remove/disable labels on tool instance stop
|
||||
- [x] 2.4 Update ToolInstance model to store labels JSON
|
||||
|
||||
## 3. Production Docker Compose
|
||||
|
||||
- [x] 3.1 Create docker-compose.prod.yml with api, web, traefik, db services
|
||||
- [x] 3.2 Configure Traefik service with Let's Encrypt certificates
|
||||
- [x] 3.3 Set up platform and tools networks
|
||||
- [x] 3.4 Add health checks for all services
|
||||
- [x] 3.5 Configure logging (JSON format, rotation)
|
||||
|
||||
## 4. Portainer Deployment
|
||||
|
||||
- [x] 4.1 Create deploy/portainer-stack.yml
|
||||
- [x] 4.2 Add Portainer-specific environment variable documentation
|
||||
- [x] 4.3 Create deploy/.env.example for production
|
||||
- [x] 4.4 Test stack deployment locally with docker compose -f docker-compose.prod.yml
|
||||
|
||||
## 5. Documentation
|
||||
|
||||
- [x] 5.1 Update docs/deployment.md with production deployment steps
|
||||
- [x] 5.2 Add Traefik configuration guide
|
||||
- [x] 5.3 Document subdomain scheme and DNS requirements
|
||||
- [x] 5.4 Update README.md with deployment section
|
||||
|
||||
## 6. Testing & Verification
|
||||
|
||||
- [x] 6.1 Test label generation for all built-in tools
|
||||
- [x] 6.2 Verify Traefik routes correctly in local stack
|
||||
- [x] 6.3 Run backend tests: `cd apps/api && pytest`
|
||||
- [x] 6.4 Run linters: `ruff check app/` and `mypy app/`
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-14
|
||||
@@ -0,0 +1,65 @@
|
||||
## Context
|
||||
|
||||
OpenCode is an AI-powered terminal-based development environment with a web interface. Unlike code-server which is a full web IDE, OpenCode provides a terminal experience accessible through the browser. This POC validates that the spawn system handles different runtime types including web terminal forwarding.
|
||||
|
||||
Current state:
|
||||
- OpenCode manifest exists in apps/api/app/tools/manifests/opencode.yml
|
||||
- No container image or runtime defined yet
|
||||
- No health reporting mechanism
|
||||
- Spawn infrastructure will be built in FN-010
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Define OpenCode as a spawnable tool
|
||||
- Provide web terminal interface in container
|
||||
- Report health status (running/idle/error)
|
||||
- Support interactive terminal sessions
|
||||
|
||||
**Non-Goals:**
|
||||
- Full task queue or job scheduler
|
||||
- Persistent process management
|
||||
- Log streaming (deferred)
|
||||
- Multi-language support beyond terminal
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Use official OpenCode Docker image**
|
||||
- Rationale: Maintained, includes AI features and web terminal
|
||||
- Alternative: Custom image - unnecessary for POC
|
||||
|
||||
**2. OpenCode runs as a persistent container**
|
||||
- Rationale: Easier to manage lifecycle (start/stop/status). Terminal sessions need persistent container.
|
||||
- Implementation: Container runs OpenCode with web interface on port 3000
|
||||
|
||||
**3. Health check via HTTP endpoint**
|
||||
- Rationale: Standard Docker health check mechanism. Traefik can use it.
|
||||
- Endpoint: `GET /` returns 200 when ready
|
||||
|
||||
**4. Workspace mounted from host (same as code-server)**
|
||||
- Rationale: Consistency. Shared workspace between tools.
|
||||
- Path: `/data/workspaces/{user_slug}/{project_slug}`
|
||||
|
||||
**5. Configs/secrets injected same as code-server**
|
||||
- Rationale: Reuse FN-009 infrastructure. No special handling needed.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
**[Risk] OpenCode container requires significant resources**
|
||||
→ Mitigation: Set resource limits (4GB RAM, 2 CPU). Document requirements.
|
||||
|
||||
**[Risk] Web terminal performance over slow connections**
|
||||
→ Mitigation: Use modern terminal emulation with compression. Document bandwidth requirements.
|
||||
|
||||
**[Risk] AI features require API keys**
|
||||
→ Mitigation: Support secret injection for API keys. Document configuration.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
No migration. New feature.
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. Should OpenCode support multiple terminal sessions?
|
||||
2. Do we pre-configure common development tools?
|
||||
3. Should OpenCode integrate with the platform's AI provider?
|
||||
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
OpenCode is an AI-powered terminal-based development environment that provides a web interface for interactive development. It demonstrates the platform's extensibility beyond standard tools like code-server. As a POC, it validates the manifest-driven spawn system with a non-trivial runtime that requires web terminal forwarding.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **OpenCode manifest**: Define the tool with terminal web interface, workspace mounts, and health checks
|
||||
- **Container image**: Reference to OpenCode image with built-in web terminal
|
||||
- **Health reporting**: Endpoint that reports tool health to the platform
|
||||
- **Spawn integration**: Reuse the spawn flow from FN-010 but with OpenCode-specific configuration
|
||||
- **Web terminal**: Support for browser-based terminal access
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `opencode-manifest`: OpenCode tool manifest with web terminal config
|
||||
- `web-terminal`: Support for browser-based terminal interfaces
|
||||
- `health-reporting`: Tool health status reporting mechanism
|
||||
|
||||
### Modified Capabilities
|
||||
- None (reuses spawn infrastructure from FN-010)
|
||||
|
||||
## Impact
|
||||
|
||||
- **apps/api/app/tools/manifests/opencode.yml**: Updated manifest
|
||||
- **apps/api/app/services/spawn.py**: Minor updates for OpenCode-specific mounts
|
||||
- **apps/web/src/**: OpenCode appears in tool selection UI
|
||||
- **Docker images**: Uses official OpenCode image
|
||||
@@ -0,0 +1,27 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Container provides web terminal interface
|
||||
The system SHALL provide a web terminal interface in the OpenCode container.
|
||||
|
||||
#### Scenario: Terminal available
|
||||
- **WHEN** the OpenCode container is running
|
||||
- **THEN** a web terminal is accessible via HTTP on port 3000
|
||||
- **AND** the user can execute shell commands through the browser
|
||||
|
||||
#### Scenario: Workspace access
|
||||
- **WHEN** the container runs
|
||||
- **THEN** the project workspace is mounted at /workspace
|
||||
- **AND** the user can read/write files in the workspace
|
||||
|
||||
### Requirement: Container supports AI features
|
||||
The system SHALL allow AI-powered development features in the OpenCode environment.
|
||||
|
||||
#### Scenario: AI assistance
|
||||
- **WHEN** the user interacts with OpenCode
|
||||
- **THEN** AI features are available for code completion and assistance
|
||||
- **AND** the user can configure AI provider settings
|
||||
|
||||
#### Scenario: Terminal session persistence
|
||||
- **WHEN** the user opens a terminal session
|
||||
- **THEN** the session persists while the container runs
|
||||
- **AND** multiple sessions can be opened
|
||||
@@ -0,0 +1,22 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Tool reports health status
|
||||
The system SHALL provide a mechanism for OpenCode to report its health.
|
||||
|
||||
#### Scenario: Health endpoint
|
||||
- **WHEN** the OpenCode container is running
|
||||
- **THEN** it exposes a / endpoint for health checks
|
||||
- **AND** returns 200 when the web terminal is ready
|
||||
|
||||
#### Scenario: Health check in Traefik
|
||||
- **WHEN** the container is spawned
|
||||
- **THEN** Traefik uses the health endpoint for routing decisions
|
||||
- **AND** unhealthy containers are removed from the load balancer
|
||||
|
||||
### Requirement: Platform tracks tool health
|
||||
The system SHALL track and display the health of OpenCode instances.
|
||||
|
||||
#### Scenario: Status display
|
||||
- **WHEN** the user views an OpenCode instance
|
||||
- **THEN** the current status is displayed (healthy, unhealthy, starting)
|
||||
- **AND** the status updates automatically
|
||||
@@ -0,0 +1,15 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: OpenCode manifest defines web terminal environment
|
||||
The system SHALL provide an OpenCode manifest with web terminal configuration.
|
||||
|
||||
#### Scenario: Manifest includes terminal config
|
||||
- **WHEN** the OpenCode manifest is loaded
|
||||
- **THEN** it specifies an OpenCode Docker image with web interface
|
||||
- **AND** it defines exposed ports for the HTTP interface (port 3000)
|
||||
- **AND** it defines volume mounts (workspace, config)
|
||||
|
||||
#### Scenario: Manifest includes health check
|
||||
- **WHEN** the manifest is used for spawning
|
||||
- **THEN** it defines a health check endpoint
|
||||
- **AND** specifies health check interval and timeout
|
||||
@@ -0,0 +1,42 @@
|
||||
## 1. Manifest Definition
|
||||
|
||||
- [x] 1.1 Create apps/api/app/tools/manifests/opencode.yml with web terminal config
|
||||
- [x] 1.2 Add Docker image (ghcr.io/opencode-ai/opencode:latest), ports (3000), volumes
|
||||
- [x] 1.3 Add health check configuration to manifest
|
||||
- [x] 1.4 Validate manifest against ToolManifest schema
|
||||
|
||||
## 2. Container Setup
|
||||
|
||||
- [ ] 2.1 Verify OpenCode image availability and configuration
|
||||
- [ ] 2.2 Document web terminal access pattern
|
||||
- [ ] 2.3 Configure environment variables for terminal support
|
||||
- [ ] 2.4 Test container locally with docker run
|
||||
- [ ] 2.5 Verify web terminal accessibility
|
||||
|
||||
## 3. Spawn Integration
|
||||
|
||||
- [ ] 3.1 Verify SpawnService (FN-010) can spawn OpenCode instances
|
||||
- [ ] 3.2 Add OpenCode-specific volume mounts (config)
|
||||
- [ ] 3.3 Test spawn via API endpoint
|
||||
- [ ] 3.4 Verify Traefik routing to OpenCode container
|
||||
|
||||
## 4. Frontend Integration
|
||||
|
||||
- [ ] 4.1 Add OpenCode to tool selection dropdown
|
||||
- [ ] 4.2 Display OpenCode-specific options in spawn form
|
||||
- [ ] 4.3 Show OpenCode instance status in detail page
|
||||
|
||||
## 5. Testing & Verification
|
||||
|
||||
- [ ] 5.1 Test terminal availability in spawned container
|
||||
- [ ] 5.2 Test web interface accessibility
|
||||
- [ ] 5.3 Test health endpoint response
|
||||
- [ ] 5.4 Verify workspace mount is accessible
|
||||
- [ ] 5.5 Run full test suite: `make test`
|
||||
- [ ] 5.6 Run linters: `make lint`
|
||||
|
||||
## 6. Documentation
|
||||
|
||||
- [ ] 6.1 Document OpenCode setup in docs/development.md
|
||||
- [ ] 6.2 Add OpenCode usage guide
|
||||
- [ ] 6.3 Document terminal configuration and AI features
|
||||
Reference in New Issue
Block a user