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
|
||||
|
||||
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/`
|
||||
Reference in New Issue
Block a user