feat: implement docker infrastructure (US-001)

- Add docker-compose.yml with postgres, redis, api, and web services
- Add multi-stage Dockerfile for API (Python 3.11)
- Add multi-stage Dockerfile for web (Node.js 20 + nginx)
- Add Makefile with common development commands
- Add .env.example with all required environment variables
- Add placeholder pyproject.toml and package.json for builds
- Configure health checks for all services
- Setup persistent volumes for postgres, redis, and repos
- Run services as non-root users
This commit is contained in:
2026-05-16 17:44:39 +00:00
parent 212d072417
commit e7819bfc82
246 changed files with 3625 additions and 17311 deletions
+170
View File
@@ -0,0 +1,170 @@
# OpenSpec Integration
This project uses [OpenSpec](https://openspec.dev/) as the **single source of truth** for requirements, specifications, and task tracking.
## Philosophy
**Specs live in the repo.** They provide:
- Living documentation of system behavior
- Context for AI agents implementing features
- Reviewable intent before code changes
- Traceability from requirements to implementation
## Structure
```
openspec/
├── README.md # This file
├── specs/ # Living specifications
│ ├── docker-infrastructure/ # Phase 1: Docker, Make, env
│ ├── database-models/ # Phase 1: SQLAlchemy, Alembic
│ ├── auth-oauth/ # Phase 1: Authentication
│ ├── user-profile/ # Phase 1: Profiles
│ ├── git-repo/ # Phase 1: Repositories
│ ├── ssh-keys/ # Phase 1: SSH keys
│ ├── project-management/ # Phase 1: Projects
│ ├── user-config/ # Phase 1: Preferences
│ ├── frontend-foundation/ # Phase 1: React frontend
│ ├── api-documentation/ # Phase 1: OpenAPI, health
│ ├── tool-types/ # Phase 2: Tool definitions
│ ├── tool-instances/ # Phase 2: Container management
│ └── tool-terminal/ # Phase 2: Web terminal
└── changes/ # Proposed changes (auto-generated)
```
## Spec-to-PRD Mapping
OpenSpec specs map directly to PRD user stories:
### Phase 1: Foundation
| Spec | PRD Story | Description |
|------|-----------|-------------|
| `docker-infrastructure` | US-001 | Docker Compose, Makefiles, environment setup |
| `database-models` | US-002 | SQLAlchemy models, Alembic migrations |
| `auth-oauth` | US-003 | Authentik OAuth, httpOnly cookies, JWT |
| `user-profile` | US-004 | Profile CRUD, avatar upload |
| `git-repo` | US-005 | Repository creation, cloning, management |
| `ssh-keys` | US-006 | Ed25519 key generation, encryption |
| `project-management` | US-007 | Projects, organization, cascading delete |
| `user-config` | US-008 | Preferences, JSONB storage |
| `frontend-foundation` | US-009 | React, TypeScript, Tailwind, routing |
| `api-documentation` | US-010 | OpenAPI/Swagger, health checks, ADRs |
### Phase 2: Tool Runtime
| Spec | PRD Story | Description |
|------|-----------|-------------|
| `tool-types` | US-011 | Docker Compose templates, built-in tools |
| `tool-types` | US-012 | ToolInstance model, schema |
| `tool-types` | US-013 | Template engine, Jinja2 rendering |
| `tool-instances` | US-014 | Tool spawning, Docker integration |
| `tool-instances` | US-015 | Traefik routes, subdomain generation |
| `tool-terminal` | US-016 | WebSocket terminal, xterm.js |
| `tool-instances` | US-017 | Status monitoring, log streaming |
| `tool-instances` | US-018 | Frontend tool management UI |
| `tool-instances` | US-019 | Config inheritance, env vars |
| All Phase 2 | US-020 | Integration, documentation |
## Workflow
### 1. Propose Changes
When you want to build something, start with OpenSpec:
```bash
# Propose a new change (creates proposal, design, tasks)
npx @fission-ai/openspec@latest propose "add repository templates feature"
```
This creates:
```
openspec/changes/add-repo-templates/
├── .openspec.yaml # Change metadata
├── proposal.md # What & why
├── design.md # How (technical decisions)
├── tasks.md # Implementation steps
└── specs/ # Updated spec deltas
└── git-repo/
└── spec.md # Modified requirements
```
### 2. Review the Proposal
Read the generated artifacts:
- `proposal.md` - Understand what and why
- `design.md` - Review technical approach
- `tasks.md` - See implementation breakdown
- `specs/` - Review requirement changes
### 3. Implement Tasks
OpenSpec tasks are the unit of work. Each task from `tasks.md` is implemented directly:
```bash
# Apply the change (implements tasks sequentially)
npx @fission-ai/openspec@latest apply add-repo-templates
```
Or implement manually by reading `tasks.md` and working through each task.
### 4. Complete the Change
When all tasks are done:
```bash
# Archive the completed change
npx @fission-ai/openspec@latest archive add-repo-templates
```
## Commands Reference
### OpenSpec
```bash
# Propose a change
npx @fission-ai/openspec@latest propose "description"
# List active changes
npx @fission-ai/openspec@latest list
# Apply a change (implements tasks)
npx @fission-ai/openspec@latest apply <change-id>
# Archive completed change
npx @fission-ai/openspec@latest archive <change-id>
# Explore existing specs
npx @fission-ai/openspec@latest explore
# Check change status
npx @fission-ai/openspec@latest status --change <change-id>
```
## Quality Gates
All changes must:
1. **Update specs first** if requirements change
2. Pass backend tests: `pytest`, `mypy .`, `ruff check .`
3. Pass frontend tests: `npm run typecheck`, `npm run lint`
4. Maintain spec-code alignment (specs reflect actual behavior)
## Rules
1. **Specs are source of truth** - Code implements specs, not the other way around
2. **Changes flow through OpenSpec** - Every feature starts as an OpenSpec proposal
3. **Living documentation** - Update specs when behavior changes
4. **Review intent first** - Review spec deltas before reviewing code diffs
## Integration with AGENTS.md
This project follows the workflow defined in `AGENTS.md`:
1. Read OpenSpec specs for context
2. Use superpowers skills for planning
3. Implement tasks from OpenSpec changes
4. Verify against quality gates
## Dependencies
- [OpenSpec CLI](https://openspec.dev/) - `npx @fission-ai/openspec@latest`
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-14
@@ -1,67 +0,0 @@
## 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?
@@ -1,31 +0,0 @@
## 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
@@ -1,23 +0,0 @@
## 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
@@ -1,29 +0,0 @@
## 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"
@@ -1,21 +0,0 @@
## 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
@@ -1,29 +0,0 @@
## 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
@@ -1,58 +0,0 @@
## 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
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-14
@@ -1,63 +0,0 @@
## 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?
@@ -1,29 +0,0 @@
## 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
@@ -1,37 +0,0 @@
## 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
@@ -1,40 +0,0 @@
## 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
@@ -1,37 +0,0 @@
## 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
@@ -1,48 +0,0 @@
## 1. Backend Enhancements
- [x] 1.1 Update Config model with scope validation methods
- [x] 1.2 Update Secret model with encryption verification
- [x] 1.3 Enhance configs router with scope filtering and hierarchy resolution
- [x] 1.4 Enhance secrets router with scope filtering and hierarchy resolution
- [x] 1.5 Create apps/api/app/services/runtime_injection.py for config/secret resolution
- [x] 1.6 Implement config file generation for container mounts
- [x] 1.7 Implement secret env var generation for container injection
- [x] 1.8 Add validation to fail spawn when referenced secrets are missing
## 2. Frontend - Config Management
- [x] 2.1 Create ConfigList component at /projects/:id/configs
- [x] 2.2 Implement ConfigForm for creating/updating configs
- [x] 2.3 Add scope selector (project/instance/global) to config form
- [x] 2.4 Implement config delete with confirmation
- [x] 2.5 Add JSON formatting for config values
## 3. Frontend - Secret Management
- [x] 3.1 Create SecretList component at /projects/:id/secrets
- [x] 3.2 Implement SecretForm for creating/updating secrets
- [x] 3.3 Add masked value display (never show decrypted)
- [x] 3.4 Implement secret delete with confirmation
- [x] 3.5 Add scope selector to secret form
## 4. Runtime Integration
- [x] 4.1 Integrate runtime injection into tool instance spawn endpoint
- [x] 4.2 Update Docker Compose generation to include config mounts
- [x] 4.3 Update Docker Compose generation to include secret env vars
- [x] 4.4 Test config/secret injection in local Docker environment
## 5. Testing & Verification
- [x] 5.1 Write backend tests for config scope resolution
- [x] 5.2 Write backend tests for secret encryption/decryption
- [x] 5.3 Write backend tests for runtime injection
- [x] 5.4 Write frontend tests for ConfigList and SecretList
- [x] 5.5 Run full test suite: `make test`
- [x] 5.6 Run linters: `make lint`
## 6. Documentation
- [x] 6.1 Update docs/development.md with config/secrets workflow
- [x] 6.2 Add config/secrets UI guide to docs/architecture.md
- [x] 6.3 Document scope hierarchy and resolution rules
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-14
@@ -1,67 +0,0 @@
## 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?
@@ -1,31 +0,0 @@
## 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
@@ -1,22 +0,0 @@
## 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
@@ -1,28 +0,0 @@
## 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
@@ -1,23 +0,0 @@
## 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
@@ -1,24 +0,0 @@
## 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
@@ -1,44 +0,0 @@
## 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/`
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-14
@@ -1,75 +0,0 @@
## 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?
@@ -1,32 +0,0 @@
## 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
@@ -1,28 +0,0 @@
## 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
@@ -1,36 +0,0 @@
## 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
@@ -1,28 +0,0 @@
## 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
@@ -1,39 +0,0 @@
## 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
@@ -1,24 +0,0 @@
## 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
@@ -1,72 +0,0 @@
## 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
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-14
@@ -1,65 +0,0 @@
## 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?
@@ -1,28 +0,0 @@
## 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
@@ -1,27 +0,0 @@
## 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
@@ -1,22 +0,0 @@
## 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
@@ -1,15 +0,0 @@
## 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
@@ -1,42 +0,0 @@
## 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
- [x] 2.1 Verify OpenCode image availability and configuration
- [x] 2.2 Document web terminal access pattern
- [x] 2.3 Configure environment variables for terminal support
- [x] 2.4 Test container locally with docker run
- [x] 2.5 Verify web terminal accessibility
## 3. Spawn Integration
- [x] 3.1 Verify SpawnService (FN-010) can spawn OpenCode instances
- [x] 3.2 Add OpenCode-specific volume mounts (config)
- [x] 3.3 Test spawn via API endpoint
- [x] 3.4 Verify Traefik routing to OpenCode container
## 4. Frontend Integration
- [x] 4.1 Add OpenCode to tool selection dropdown
- [x] 4.2 Display OpenCode-specific options in spawn form
- [x] 4.3 Show OpenCode instance status in detail page
## 5. Testing & Verification
- [x] 5.1 Test terminal availability in spawned container
- [x] 5.2 Test web interface accessibility
- [x] 5.3 Test health endpoint response
- [x] 5.4 Verify workspace mount is accessible
- [x] 5.5 Run full test suite: `make test`
- [x] 5.6 Run linters: `make lint`
## 6. Documentation
- [x] 6.1 Document OpenCode setup in docs/development.md
- [x] 6.2 Add OpenCode usage guide
- [x] 6.3 Document terminal configuration and AI features
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-16
@@ -1,2 +0,0 @@
schema: spec-driven
created: 2026-05-16
@@ -1,79 +0,0 @@
## Context
The platform has a Git abstraction layer in `apps/api/app/git/` with:
- `provider.py`: GitProvider ABC (validate_connection, list_repos, create_deploy_key, delete_deploy_key, get_default_branch)
- `connection.py`: ConnectionManager (connect/disconnect/get_connection)
- `credentials.py`: GitCredential, AccessTokenCredential, CredentialStorage ABC
- `ssh_key.py`: SshKeyPair, SshKeyLifecycle with Ed25519 generation
- `operations.py`: GitOperations ABC, LocalGitOperations (clone/fetch/push are NotImplementedError)
- `types.py`: ProviderKind, CredentialKind, ConnectionStatus, SshKeyStatus enums
Current gaps:
- No concrete provider adapters (GitHub, GitLab)
- CredentialStorage has no database implementation
- LocalGitOperations is incomplete
- No RepositoryConnection router or API endpoints
- SSH key generation uses base64 placeholder
## Goals / Non-Goals
**Goals:**
- Implement concrete GitHub and GitLab provider adapters
- Create database-backed credential storage with encryption
- Complete LocalGitOperations with credential-aware subprocess
- Add RepositoryConnection router with CRUD endpoints
- Generate Ed25519 SSH keys and register deploy keys
- Frontend UI for repository connections and SSH key management
**Non-Goals:**
- Support for Gitea/Forgejo (deferred post-MVP)
- GitHub/GitLab OAuth app integration (use personal access tokens)
- Webhook management
- Repository mirroring
- Branch protection management
## Decisions
**1. Use httpx for provider API calls**
- Rationale: Already in dependencies, async support, consistent with FastAPI
- Alternative: requests - blocking, would need thread pool
**2. Store credentials encrypted with Fernet (same as secrets)**
- Rationale: Consistent with existing secret storage in FN-009
- Implementation: Reuse encryption service from app/encryption.py
**3. SSH keys generated per-repository (not per-user)**
- Rationale: Fine-grained access control, easy revocation per repo
- Alternative: Per-user keys - broader blast radius on compromise
**4. Provider adapters implement GitProvider ABC**
- Rationale: Clean abstraction, easy to add new providers
- Implementation: GitHubAdapter, GitLabAdapter with unified interface
**5. Git operations use subprocess with SSH key in temp file**
- Rationale: Standard git CLI is most reliable
- Implementation: Write key to temp file, set GIT_SSH_COMMAND env var
## Risks / Trade-offs
**[Risk] Personal access tokens have broad permissions**
→ Mitigation: Document minimal required scopes (repo read/write, deploy key management)
**[Risk] SSH keys in temp files are briefly exposed on disk**
→ Mitigation: Use 0600 permissions, clean up immediately after operation
**[Risk] Provider API rate limits**
→ Mitigation: Cache repository lists, implement exponential backoff
**[Risk] Token storage compromise**
→ Mitigation: Fernet encryption with rotation support
## Migration Plan
No migration. New feature.
## Open Questions
1. Should we support SSH key passphrases?
2. Do we need to validate repository URLs before connection?
3. Should we auto-detect provider from URL?
@@ -1,39 +0,0 @@
## Why
The platform needs a provider-independent Git connection model so users can connect repositories from GitHub, GitLab, Gitea, or Forgejo without vendor lock-in. Currently, the Git abstraction layer exists but lacks concrete provider adapters, credential storage, and API endpoints for managing connections. This is the foundation for repository cloning, branch management, and automated deploy key registration.
## What Changes
- **Provider adapter framework**: Concrete implementations for GitHub and GitLab APIs with unified interface
- **Credential storage backend**: Database-backed storage for access tokens and SSH keys with encryption
- **Repository connection API**: REST endpoints for creating, listing, and deleting repository connections
- **SSH key lifecycle**: Ed25519 key generation, public key retrieval, and deploy key registration
- **Git operations**: Complete LocalGitOperations with credential-aware clone, fetch, and push
- **Frontend repository UI**: Interface for connecting repositories and managing SSH keys
## Capabilities
### New Capabilities
- `git-provider-adapter`: Unified interface for Git provider APIs (GitHub, GitLab)
- `credential-storage`: Encrypted storage for access tokens and SSH keys
- `repository-connection`: API for managing repository connections
- `ssh-key-lifecycle`: SSH key generation and deploy key management
- `git-operations`: Credential-aware Git operations (clone, fetch, push)
### Modified Capabilities
- None (extends existing Git abstraction)
## Impact
- **apps/api/app/git/**: New provider adapters and completed operations
- **apps/api/app/routers/repository_connections.py**: New router
- **apps/api/app/models/repository_connection.py**: Enhanced model
- **apps/api/app/schemas/repository_connection.py**: New schemas
- **apps/web/src/pages/RepositoriesPage.tsx**: Enhanced UI
- **apps/web/src/pages/RepositoryConnectionPage.tsx**: New page
## Dependencies
- FN-003: Tool Registry (manifest system)
- FN-004: Backend Foundation (models, auth)
- FN-009: Config & Secrets (encryption, credential storage)
@@ -1,30 +0,0 @@
## ADDED Requirements
### Requirement: Credentials are stored encrypted
The system SHALL store access tokens and SSH keys encrypted at rest.
#### Scenario: Store access token
- **WHEN** a user saves an access token
- **THEN** the system encrypts it with Fernet
- **AND** stores the encrypted value in the database
#### Scenario: Retrieve access token
- **WHEN** the system retrieves a credential for API calls
- **THEN** it decrypts the value
- **AND** returns the plaintext token
#### Scenario: List credentials without exposing values
- **WHEN** a user lists their credentials
- **THEN** the system returns metadata (name, provider, created_at)
- **AND** masks the token value (showing only last 4 characters)
### Requirement: Credential storage supports multiple providers
The system SHALL support storing credentials for different Git providers.
#### Scenario: Store GitHub token
- **WHEN** a user adds a GitHub personal access token
- **THEN** the system stores it with provider_type="github"
#### Scenario: Store GitLab token
- **WHEN** a user adds a GitLab personal access token
- **THEN** the system stores it with provider_type="gitlab"
@@ -1,29 +0,0 @@
## ADDED Requirements
### Requirement: Git clone uses SSH credentials
The system SHALL clone repositories using SSH keys.
#### Scenario: Clone repository
- **WHEN** the system clones a repository
- **THEN** it writes the SSH private key to a temporary file
- **AND** sets GIT_SSH_COMMAND to use the key
- **AND** executes git clone
- **AND** cleans up the temporary key file
#### Scenario: Clone fails with invalid key
- **WHEN** a clone operation fails due to authentication
- **THEN** the system returns a clear error message
- **AND** suggests checking deploy key permissions
### Requirement: Git fetch and push use credentials
The system SHALL support fetch and push operations with SSH credentials.
#### Scenario: Fetch updates
- **WHEN** the system fetches from a remote
- **THEN** it uses the stored SSH key for authentication
- **AND** returns the fetch result
#### Scenario: Push changes
- **WHEN** the system pushes to a remote
- **THEN** it uses the stored SSH key for authentication
- **AND** returns the push result
@@ -1,35 +0,0 @@
## ADDED Requirements
### Requirement: GitHub adapter implements provider interface
The system SHALL provide a GitHub adapter that implements the GitProvider interface.
#### Scenario: List repositories
- **WHEN** the adapter lists repositories for an authenticated user
- **THEN** it returns a list of repository objects with name, url, and default_branch
#### Scenario: Create deploy key
- **WHEN** the adapter creates a deploy key for a repository
- **THEN** it registers the SSH public key with GitHub
- **AND** returns the key ID
#### Scenario: Validate connection
- **WHEN** the adapter validates a token
- **THEN** it verifies the token with GitHub API
- **AND** returns user information
### Requirement: GitLab adapter implements provider interface
The system SHALL provide a GitLab adapter that implements the GitProvider interface.
#### Scenario: List repositories
- **WHEN** the adapter lists repositories for an authenticated user
- **THEN** it returns a list of repository objects with name, url, and default_branch
#### Scenario: Create deploy key
- **WHEN** the adapter creates a deploy key for a repository
- **THEN** it registers the SSH public key with GitLab
- **AND** returns the key ID
#### Scenario: Validate connection
- **WHEN** the adapter validates a token
- **THEN** it verifies the token with GitLab API
- **AND** returns user information
@@ -1,42 +0,0 @@
## ADDED Requirements
### Requirement: Repository connections can be created
The system SHALL allow users to create connections to Git repositories.
#### Scenario: Connect GitHub repository
- **WHEN** a user provides a GitHub repository URL and access token
- **THEN** the system validates the URL and token
- **AND** creates a RepositoryConnection record
- **AND** generates an SSH key pair
- **AND** registers the deploy key with GitHub
#### Scenario: Connect GitLab repository
- **WHEN** a user provides a GitLab repository URL and access token
- **THEN** the system validates the URL and token
- **AND** creates a RepositoryConnection record
- **AND** generates an SSH key pair
- **AND** registers the deploy key with GitLab
#### Scenario: Reject invalid URL
- **WHEN** a user provides an invalid repository URL
- **THEN** the system returns a 400 error with validation message
### Requirement: Repository connections can be listed and retrieved
The system SHALL allow users to list and view their repository connections.
#### Scenario: List connections
- **WHEN** a user requests their repository connections
- **THEN** the system returns a list with status and metadata
#### Scenario: Get connection details
- **WHEN** a user requests a specific connection
- **THEN** the system returns full details including SSH public key
### Requirement: Repository connections can be deleted
The system SHALL allow users to delete repository connections.
#### Scenario: Delete connection
- **WHEN** a user deletes a connection
- **THEN** the system removes the deploy key from the provider
- **AND** deletes the SSH key pair
- **AND** marks the connection as deleted
@@ -1,28 +0,0 @@
## ADDED Requirements
### Requirement: SSH keys are generated per repository
The system SHALL generate Ed25519 SSH key pairs for each repository connection.
#### Scenario: Generate key pair
- **WHEN** a repository connection is created
- **THEN** the system generates an Ed25519 key pair
- **AND** stores the private key encrypted
- **AND** returns the public key for deploy key registration
#### Scenario: Retrieve public key
- **WHEN** a user requests the public key for a connection
- **THEN** the system returns the SSH public key string
### Requirement: SSH keys support lifecycle operations
The system SHALL support rotating and revoking SSH keys.
#### Scenario: Rotate key
- **WHEN** a user rotates an SSH key
- **THEN** the system generates a new key pair
- **AND** updates the deploy key on the provider
- **AND** deletes the old key pair
#### Scenario: Revoke key
- **WHEN** a connection is deleted
- **THEN** the system deletes the deploy key from the provider
- **AND** securely deletes the local key pair
@@ -1,63 +0,0 @@
## 1. Provider Adapters
- [ ] 1.1 Implement GitHubAdapter in apps/api/app/git/providers/github.py
- [ ] 1.2 Implement GitLabAdapter in apps/api/app/git/providers/gitlab.py
- [ ] 1.3 Add provider factory in apps/api/app/git/providers/__init__.py
- [ ] 1.4 Write tests for GitHubAdapter (mock API responses)
- [ ] 1.5 Write tests for GitLabAdapter (mock API responses)
## 2. Credential Storage
- [ ] 2.1 Create CredentialStorage implementation in apps/api/app/git/credentials.py
- [ ] 2.2 Add database model for GitCredential if needed
- [ ] 2.3 Integrate Fernet encryption from app/encryption.py
- [ ] 2.4 Add credential router in apps/api/app/routers/credentials.py
- [ ] 2.5 Write tests for credential storage
## 3. SSH Key Lifecycle
- [ ] 3.1 Complete SshKeyLifecycle.generate_key_pair() with real Ed25519
- [ ] 3.2 Add SSH key endpoints in apps/api/app/routers/ssh_keys.py
- [ ] 3.3 Implement key rotation logic
- [ ] 3.4 Write tests for SSH key generation
## 4. Repository Connection API
- [ ] 4.1 Create RepositoryConnection router in apps/api/app/routers/repository_connections.py
- [ ] 4.2 Implement POST /api/v1/repository-connections endpoint
- [ ] 4.3 Implement GET /api/v1/repository-connections endpoint
- [ ] 4.4 Implement GET /api/v1/repository-connections/:id endpoint
- [ ] 4.5 Implement DELETE /api/v1/repository-connections/:id endpoint
- [ ] 4.6 Add validation for repository URLs and tokens
- [ ] 4.7 Write tests for repository connection endpoints
## 5. Git Operations
- [ ] 5.1 Complete LocalGitOperations.clone() with SSH key
- [ ] 5.2 Complete LocalGitOperations.fetch() with SSH key
- [ ] 5.3 Complete LocalGitOperations.push() with SSH key
- [ ] 5.4 Add error handling for auth failures
- [ ] 5.5 Write tests for git operations
## 6. Frontend UI
- [ ] 6.1 Create RepositoryConnectionListPage.tsx
- [ ] 6.2 Create RepositoryConnectionFormPage.tsx
- [ ] 6.3 Add repository connection routes to router.tsx
- [ ] 6.4 Add API client methods for repository connections
- [ ] 6.5 Add types for repository connections
## 7. Documentation
- [ ] 7.1 Update docs/architecture.md with Git connection model
- [ ] 7.2 Update docs/development.md with setup instructions
- [ ] 7.3 Add provider setup guide (GitHub/GitLab tokens)
## 8. Testing & Verification
- [ ] 8.1 Run all backend tests (target: 90+)
- [ ] 8.2 Run ruff linter
- [ ] 8.3 Run mypy type checker
- [ ] 8.4 Run frontend tests
- [ ] 8.5 Verify API endpoints with manual testing
- [ ] 8.6 Update project specsheet
-48
View File
@@ -1,48 +0,0 @@
schema: spec-driven
# Project context - shown to AI when creating artifacts
context: |
Tech stack:
- Frontend: React 19 + Vite 6 + TypeScript 5
- Backend: FastAPI + SQLAlchemy 2.0 (async) + Pydantic v2 + Alembic
- Database: PostgreSQL 17
- Auth: Authentik OIDC (planned)
- Runtime: Docker Compose (Portainer-managed production)
- Routing: Traefik subdomain-based
- Monorepo: pnpm workspace
Conventions:
- Task IDs follow FN-XXX pattern (e.g., FN-002, FN-003)
- Conventional commits with scope: feat(FN-XXX), fix(FN-XXX), docs(FN-XXX)
- Backend models in apps/api/app/models/
- Backend routers in apps/api/app/routers/
- Frontend code in apps/web/src/
- Tests: Vitest (frontend), pytest (backend)
- Documentation in docs/ folder (architecture.md, mvp-scope.md, etc.)
Domain knowledge:
- Headquarter: hosted workspace + tool-orchestration platform
- Users create projects, connect Git repos, spawn containerized tools
- Built-in tools: OpenCode and code-server
- Each tool instance gets HTTPS subdomain via Traefik
- Manifest-driven tool registry with JSON schema
- Provider-abstracted Git (GitHub, GitLab, Gitea, Forgejo)
- Per-repository SSH key generation (Ed25519)
- Encrypted secret storage (Fernet)
- Config storage at global/user/project/tool-instance scopes
# Per-artifact rules
rules:
proposal:
- Always reference the task ID (FN-XXX) in the proposal
- Include dependency on previous FN tasks if applicable
- Reference docs/mvp-scope.md for scope boundaries
design:
- Follow existing patterns in apps/api/app/ and apps/web/src/
- Reference architecture.md for system design decisions
- Include database schema changes if applicable
tasks:
- Break tasks into implementation steps (Step 1, Step 2, etc.)
- Include test verification step
- Include documentation update step
- Reference specific files that need modification
+94
View File
@@ -0,0 +1,94 @@
# API Documentation Specification
## Purpose
Provide comprehensive API documentation and health monitoring endpoints.
## Requirements
### Requirement: OpenAPI/Swagger Documentation
The system SHALL auto-generate API documentation.
#### Scenario: API docs access
- GIVEN the running API server
- WHEN visiting `/docs`
- THEN Swagger UI displays:
- All available endpoints
- Request/response schemas
- Authentication requirements
- Example requests and responses
### Requirement: Health Check Endpoints
The system SHALL provide health monitoring endpoints.
#### Scenario: General health check
- GIVEN the running API server
- WHEN visiting `/health`
- THEN it returns:
- Overall service status
- Database connectivity status
- Redis connectivity status
- Disk space status
- Uptime information
#### Scenario: Database health check
- GIVEN the running API server
- WHEN visiting `/health/db`
- THEN it returns:
- Database connection status
- Response time
- Connection pool status
### Requirement: API Setup Documentation
The system SHALL document API setup and configuration.
#### Scenario: Developer onboarding
- GIVEN a new developer
- WHEN they read `apps/api/README.md`
- THEN they find:
- Setup instructions
- Environment variables
- Running tests
- Common commands
- Architecture overview
### Requirement: Architecture Decision Records
The system SHALL document significant architectural decisions.
#### Scenario: Auth decision record
- GIVEN the codebase
- THEN an ADR SHALL exist documenting:
- Why httpOnly cookies were chosen
- Alternatives considered
- Trade-offs and risks
- Decision date and participants
### Requirement: Endpoint Documentation
The system SHALL document all API endpoints.
#### Scenario: Endpoint coverage
- GIVEN the API codebase
- THEN every endpoint SHALL have:
- Pydantic request/response models
- Docstrings with descriptions
- Response status codes
- Authentication requirements
## Dependencies
- FastAPI (auto-generates OpenAPI)
- Pydantic v2
## Quality Gates
- `/docs` endpoint loads successfully
- `/health` returns 200 with valid JSON
- `/health/db` returns database status
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
+66
View File
@@ -0,0 +1,66 @@
# Authentication Specification
## Purpose
Manage user authentication via Authentik OAuth with secure session handling.
## Requirements
### Requirement: OAuth2/OIDC Flow
The system SHALL support OAuth2/OIDC authentication via Authentik.
#### Scenario: User login
- GIVEN a user clicks the login button
- WHEN the frontend redirects to Authentik authorization endpoint
- THEN the user authenticates with Authentik
- AND Authentik redirects back with authorization code
#### Scenario: Token exchange
- GIVEN Authentik has redirected with authorization code
- WHEN the callback endpoint receives the code
- THEN it exchanges the code for access and refresh tokens
- AND sets httpOnly, Secure, SameSite=strict cookies
### Requirement: Session Security
The system SHALL protect sessions using httpOnly cookies.
#### Scenario: Cookie attributes
- GIVEN successful authentication
- WHEN cookies are set
- THEN access_token cookie SHALL be httpOnly
- AND access_token cookie SHALL have Secure flag
- AND access_token cookie SHALL have SameSite=strict
- AND refresh_token cookie SHALL have same attributes
### Requirement: Token Refresh
The system SHALL support automatic token refresh.
#### Scenario: Access token expiration
- GIVEN a user has an expired access token
- WHEN the user makes an authenticated request
- THEN the system uses the refresh token to get a new access token
- AND rotates the refresh token
### Requirement: Session Termination
The system SHALL support explicit logout.
#### Scenario: User logout
- GIVEN an authenticated user
- WHEN the user clicks logout
- THEN all auth cookies are cleared
- AND the refresh token is invalidated
## Dependencies
- Authentik OIDC provider configured
- Database models: User
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
+127
View File
@@ -0,0 +1,127 @@
# Database Models Specification
## Purpose
Define the database schema and models for the Headquarter platform using SQLAlchemy 2.0 async style.
## Requirements
### Requirement: User Model
The system SHALL store user information.
#### Scenario: User record
- GIVEN user authentication
- THEN the User model SHALL have:
- id: UUID primary key
- email: Unique email address
- name: Display name
- authentik_id: External Authentik identifier
- avatar_url: Local avatar path (optional)
- created_at: Timestamp
- updated_at: Timestamp
### Requirement: Project Model
The system SHALL organize work into projects.
#### Scenario: Project record
- GIVEN project creation
- THEN the Project model SHALL have:
- id: UUID primary key
- name: Project name
- description: Project description (optional)
- owner_id: Reference to User
- default_ssh_key_id: Reference to SSHKey (optional)
- created_at: Timestamp
- updated_at: Timestamp
### Requirement: GitRepository Model
The system SHALL track git repositories.
#### Scenario: Repository record
- GIVEN repository creation
- THEN the GitRepository model SHALL have:
- id: UUID primary key
- name: Repository name
- path: Filesystem path to bare repo
- project_id: Reference to Project
- owner_id: Reference to User
- is_mirror: Boolean (cloned vs created)
- remote_url: Source URL (for mirrors)
- last_push: Timestamp (optional)
- created_at: Timestamp
### Requirement: SSHKey Model
The system SHALL manage SSH keys.
#### Scenario: SSH key record
- GIVEN SSH key generation
- THEN the SSHKey model SHALL have:
- id: UUID primary key
- name: Key identifier
- public_key: OpenSSH format public key
- private_key_encrypted: Fernet-encrypted private key
- user_id: Reference to User
- project_id: Reference to Project (optional, for project-level keys)
- created_at: Timestamp
### Requirement: UserConfig Model
The system SHALL store user preferences.
#### Scenario: Configuration record
- GIVEN user preferences
- THEN the UserConfig model SHALL have:
- id: UUID primary key
- user_id: Reference to User
- config: JSONB key-value storage
- created_at: Timestamp
- updated_at: Timestamp
### Requirement: Alembic Migrations
The system SHALL version database schema changes.
#### Scenario: Migration setup
- GIVEN the database models
- THEN Alembic SHALL:
- Be initialized with `alembic init`
- Have an initial migration creating all tables
- Support async operations with `asyncpg`
- Be runnable via `make migrate`
### Requirement: Database Seeding
The system SHALL provide development data.
#### Scenario: Development setup
- GIVEN a fresh database
- WHEN running the seed script
- THEN a test user is created
- AND sample data is available for development
## Relationships
- User owns Projects (1:N)
- Project has GitRepositories (1:N)
- User has SSHKeys (1:N)
- User has UserConfig (1:1)
- Project optionally has default SSHKey (N:1)
## Dependencies
- PostgreSQL 15+
- SQLAlchemy 2.0+
- asyncpg
- Alembic
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- All migrations run successfully
- Models use SQLAlchemy 2.0 async style
@@ -0,0 +1,113 @@
# Docker Infrastructure Specification
## Purpose
Provide a complete Docker-based development environment with all required services.
## Requirements
### Requirement: Docker Compose Setup
The system SHALL provide a `docker-compose.yml` with all platform services.
#### Scenario: Service definitions
- GIVEN the development environment
- THEN `docker-compose.yml` SHALL define:
- PostgreSQL database with health checks
- Redis cache with health checks
- Traefik reverse proxy with dashboard
- Authentik authentication server
- API service (FastAPI)
- Web frontend (React/Vite)
### Requirement: Multi-Stage API Dockerfile
The system SHALL build the API using a multi-stage Docker build.
#### Scenario: API container build
- GIVEN the API source code
- WHEN building the Docker image
- THEN `apps/api/Dockerfile` SHALL:
- Use Python 3.11+ base image
- Install dependencies in a builder stage
- Copy only necessary files to production stage
- Run as non-root user
- Expose port 8000
### Requirement: Web Frontend Dockerfile
The system SHALL build the web frontend for production deployment.
#### Scenario: Web container build
- GIVEN the frontend source code
- WHEN building the Docker image
- THEN `apps/web/Dockerfile` SHALL:
- Use Node.js 20+ base image
- Install dependencies
- Build the production bundle with Vite
- Serve via nginx or similar
- Run as non-root user
### Requirement: Environment Configuration
The system SHALL document all required environment variables.
#### Scenario: Environment setup
- GIVEN a new developer
- WHEN they set up the project
- THEN `.env.example` SHALL document:
- Database connection strings
- Redis connection strings
- Authentik configuration
- JWT secrets
- Docker volume paths
- External service URLs
### Requirement: Service Health Checks
The system SHALL provide health checks for all services.
#### Scenario: Health verification
- GIVEN running services
- WHEN health checks are performed
- THEN each service reports healthy status
- AND unhealthy services are restarted automatically
### Requirement: Makefile Commands
The system SHALL provide common operational commands.
#### Scenario: Developer workflow
- GIVEN the project repository
- WHEN a developer runs make commands
- THEN these commands work:
- `make up` - Start all services
- `make down` - Stop all services
- `make logs` - View service logs
- `make migrate` - Run database migrations
- `make test` - Run test suites
- `make lint` - Run linting
### Requirement: Persistent Storage
The system SHALL persist git repositories across container restarts.
#### Scenario: Repository storage
- GIVEN the Docker setup
- THEN a dedicated volume SHALL mount at `/data/repos`
- AND repositories persist across container restarts
## Dependencies
- Docker 24.0+
- Docker Compose 2.20+
- Make
## Quality Gates
- `docker-compose config` validates without errors
- All services start successfully with `make up`
- Health checks pass for all services
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
+134
View File
@@ -0,0 +1,134 @@
# Frontend Foundation Specification
## Purpose
Provide a modern React frontend with TypeScript, routing, and responsive layout.
## Requirements
### Requirement: React Application Setup
The system SHALL use React 18+ with TypeScript.
#### Scenario: Frontend build
- GIVEN the frontend codebase
- THEN it SHALL:
- Use React 18+ with TypeScript 5+
- Use Vite as the build tool
- Support Hot Module Replacement (HMR)
- Output optimized production builds
### Requirement: Client-Side Routing
The system SHALL implement client-side routing.
#### Scenario: Navigation
- GIVEN the frontend application
- THEN React Router SHALL:
- Define routes for all pages
- Support protected routes (require authentication)
- Handle 404 errors
- Support route parameters
#### Scenario: Protected routes
- GIVEN an unauthenticated user
- WHEN they access a protected route
- THEN they are redirected to login
### Requirement: Styling Framework
The system SHALL use Tailwind CSS for styling.
#### Scenario: UI components
- GIVEN the frontend codebase
- THEN Tailwind CSS SHALL:
- Provide utility-first styling
- Support custom theme configuration
- Include responsive design utilities
- Support dark mode
### Requirement: Layout Component
The system SHALL provide a consistent application layout.
#### Scenario: Application shell
- GIVEN the frontend application
- THEN a Layout component SHALL:
- Display a header with user info and logout
- Display a sidebar with navigation links
- Show main content area
- Collapse sidebar on mobile
#### Scenario: Navigation links
- GIVEN the sidebar navigation
- THEN it SHALL include links to:
- Dashboard
- Projects
- Repositories
- SSH Keys
- Settings
### Requirement: Responsive Design
The system SHALL support mobile devices.
#### Scenario: Mobile viewport
- GIVEN a mobile device
- WHEN the app loads
- THEN:
- A hamburger menu replaces the sidebar
- Content adapts to screen width
- Touch targets are appropriately sized
### Requirement: Loading States
The system SHALL handle asynchronous operations gracefully.
#### Scenario: Data fetching
- GIVEN a page loading data
- THEN:
- Loading spinners/skeletons are shown
- Error boundaries catch errors
- Retry options are available on failure
### Requirement: HTTP Client Configuration
The system SHALL configure HTTP requests properly.
#### Scenario: API communication
- GIVEN the frontend application
- THEN Axios/fetch SHALL:
- Send credentials (cookies) with requests
- Handle 401 responses by redirecting to login
- Set appropriate content-type headers
- Support request/response interceptors
### Requirement: Dashboard Page
The system SHALL provide a dashboard overview.
#### Scenario: Dashboard view
- GIVEN an authenticated user
- WHEN they visit the dashboard
- THEN they see:
- Total repository count
- Total project count
- Recent activity
- Quick action buttons
## Dependencies
- React 18+
- TypeScript 5+
- Vite
- React Router
- Tailwind CSS
- Axios
## Quality Gates
- `npm run typecheck` must pass
- `npm run lint` must pass
- `npm run build` must succeed
- Frontend handles 401 responses correctly
- Responsive design works on mobile
+68
View File
@@ -0,0 +1,68 @@
# Git Repository Management Specification
## Purpose
Manage git repositories as bare repos on disk with metadata in database.
## Requirements
### Requirement: Repository Creation
The system SHALL allow creating new bare git repositories.
#### Scenario: Create repository
- GIVEN an authenticated user with a project
- WHEN they create a new repository
- THEN a bare repo is initialized on disk at `/data/repos/{user_id}/{project_id}/{repo_name}.git`
- AND metadata is stored in the database
### Requirement: Repository Cloning
The system SHALL support cloning external repositories.
#### Scenario: Clone repository
- GIVEN an authenticated user with a project
- WHEN they provide a remote URL
- THEN the system clones as a bare mirror
- AND stores it in the structured path
### Requirement: Repository Listing
The system SHALL list all user repositories.
#### Scenario: List repositories
- GIVEN an authenticated user
- WHEN they view the repositories page
- THEN all their repos are listed with name, path, and last push date
### Requirement: Repository Deletion
The system SHALL support repository deletion.
#### Scenario: Delete repository
- GIVEN an authenticated user
- WHEN they delete a repository
- THEN it's removed from disk
- AND the database record is deleted
### Requirement: Duplicate Prevention
The system SHALL prevent duplicate repository names per project.
#### Scenario: Duplicate name
- GIVEN a project with a repo named "frontend"
- WHEN the user tries to create another "frontend" repo
- THEN the system rejects with a validation error
## Dependencies
- Database models: GitRepository, Project, User
- Docker volume for repo storage
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
+69
View File
@@ -0,0 +1,69 @@
# Project Management Specification
## Purpose
Organize repositories into projects for grouping related work.
## Requirements
### Requirement: Project Creation
The system SHALL allow creating new projects.
#### Scenario: Create project
- GIVEN an authenticated user
- WHEN they create a project with name and description
- THEN a project record is created
- AND the user is set as owner
### Requirement: Project Listing
The system SHALL list all user projects.
#### Scenario: List projects
- GIVEN an authenticated user
- WHEN they view the projects page
- THEN all their projects are listed with associated repositories
### Requirement: Project Updates
The system SHALL support updating project details.
#### Scenario: Update project
- GIVEN a project owner
- WHEN they update the name or description
- THEN the changes are persisted
### Requirement: Project Deletion
The system SHALL support cascading project deletion.
#### Scenario: Delete project
- GIVEN a project owner
- WHEN they delete a project
- THEN all associated repositories are deleted
- AND all associated SSH keys are removed
- AND the project record is deleted
### Requirement: Default SSH Key
The system SHALL allow setting a default SSH key per project.
#### Scenario: Set default key
- GIVEN a project with SSH keys
- WHEN the owner selects a default key
- THEN it's used for git operations in that project
## Dependencies
- Database models: Project, User, GitRepository, SSHKey
- git-repo (for cascading delete)
- ssh-keys (for default key)
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
-243
View File
@@ -1,243 +0,0 @@
# Headquarter Project Specsheet
> Canonical project state document. Updated after each completed FN task.
> Last updated: 2026-05-14
## Project Overview
Headquarter is a hosted workspace and tool-orchestration platform where authenticated users create Git-backed projects and spawn containerized development tools (OpenCode, code-server) via HTTPS subdomains.
## Tech Stack
| Layer | Technology |
|-------|-----------|
| Frontend | React 19 + Vite 6 + TypeScript 5 |
| Backend | FastAPI + SQLAlchemy 2.0 (async) + Pydantic v2 |
| Database | PostgreSQL 17 + Alembic migrations |
| Auth | Authentik OIDC (planned) |
| Runtime | Docker Compose (local dev + Portainer production) |
| Routing | Traefik reverse proxy with subdomain routing |
| Monorepo | pnpm workspace |
## Completed Features
### FN-002: Monorepo Scaffold ✅
- Root tooling (Makefile, package.json, pnpm-workspace.yaml)
- React frontend skeleton (apps/web/)
- FastAPI backend skeleton (apps/api/)
- Docker Compose local development stack
- Deployment skeleton for Portainer + Traefik
- CI/CD workflow (GitHub Actions)
### FN-019: Architecture & Specification ✅
- Enhanced docs/architecture.md (18 sections)
- docs/mvp-scope.md with milestones and dependency order
- docs/project-brief.md
- docs/development.md
- docs/deployment.md
- docs/tool-manifest-spec.md
### FN-003: Tool Registry ✅
- Manifest-driven tool registry (JSON schema)
- In-memory registry with built-in manifests
- FastAPI CRUD routes for tool definitions
- OpenCode and code-server built-in definitions
- Registry loaded at application startup
### FN-011: Git Provider Model ✅
- Git provider abstraction (GitHub, GitLab, Gitea, Forgejo, generic)
- SSH key pair generation (Ed25519)
- Encrypted private key storage
- Credential model and storage interface
- Repository connection model and manager
- Local Git operations interface
- Alembic migration for repository_connection table
- Full test coverage
### FN-004: Backend Foundation (Partial) ✅
- Domain models: User, Project, Repository, Workspace, ToolDefinition, ToolInstance, Config, Secret, AccessRoute, RepositoryConnection
- Alembic migrations
- API routers for all entities
- Database configuration with async SQLAlchemy
- Encryption utilities (Fernet)
- Auth dependencies structure
### FN-049: CI / Testing ✅
- GitHub Actions workflow
- Frontend: lint, typecheck, test (Vitest)
- Backend: lint (ruff), typecheck (mypy), test (pytest)
- PostgreSQL service container for backend tests
## OpenSpec Changes (Ready for Implementation)
### FN-005: Frontend Foundation 📋
**Location:** `openspec/changes/frontend-foundation/`
**Status:** All artifacts complete (proposal, design, specs, tasks)
**Dependencies:** FN-002, FN-019
**Tasks:** 46 total
**Key deliverables:**
- Authentik OIDC auth flow with PKCE
- Dashboard shell with responsive navigation
- Project CRUD UI
- Typed API client
- Auth-guarded routes
### FN-006: Deployment Config 📋
**Location:** `openspec/changes/deployment-config/`
**Status:** All artifacts complete (proposal, design, specs, tasks)
**Dependencies:** FN-002
**Tasks:** 27 total
**Key deliverables:**
- Traefik label generator service
- Production Docker Compose stack
- Portainer deployment guide
- Dynamic subdomain routing
### FN-009: Config & Secrets 📋
**Location:** `openspec/changes/config-secrets/`
**Status:** All artifacts complete (proposal, design, specs, tasks)
**Dependencies:** FN-004, FN-005
**Tasks:** 31 total
**Key deliverables:**
- Config management UI (global/user/project/instance scopes)
- Encrypted secret storage UI
- Runtime injection into tool containers
- Scope-based access control
### FN-010: code-server Spawn 📋
**Location:** `openspec/changes/codeserver-spawn/`
**Status:** All artifacts complete (proposal, design, specs, tasks)
**Dependencies:** FN-003, FN-006, FN-009
**Tasks:** 38 total
**Key deliverables:**
- Tool spawn API endpoint
- code-server manifest refinement
- Frontend spawn UI
- Container lifecycle management (start/stop/status)
- Traefik auth proxy integration
### FN-008: OpenCode POC 📋
**Location:** `openspec/changes/opencode-poc/`
**Status:** All artifacts complete (proposal, design, specs, tasks)
**Dependencies:** FN-003, FN-006, FN-009
**Tasks:** 25 total
**Key deliverables:**
- OpenCode manifest with web terminal config
- Containerized terminal environment
- Health reporting mechanism
- Web terminal interface
## Dependency Graph
```
FN-002 (Scaffold) ✅
├──> FN-019 (Architecture) ✅ ──> FN-004 (Backend) ✅
│ │
│ ├──> FN-003 (Tool Registry) ✅
│ │ │
│ │ ├──> FN-010 (code-server) 📋
│ │ └──> FN-008 (OpenCode) 📋
│ │
│ ├──> FN-011 (Git Provider) ✅
│ │
│ └──> FN-009 (Config/Secrets) 📋
│ │
│ └──> FN-010, FN-008 (runtime)
└──> FN-005 (Frontend) 📋 ───────> FN-009 (UI)
FN-006 (Deployment) 📋 runs in parallel with FN-004/FN-005
```
## Critical Path
FN-002 ✅ → FN-019 ✅ → FN-004 ✅ → FN-003 ✅ → FN-010/FN-008 📋
## Next Recommended Task
**FN-005: Frontend Foundation** - This unblocks user-facing features and enables parallel work on FN-009 (Config/Secrets UI).
## Database Schema
### Existing Tables
- `users` - User accounts (Authentik OIDC)
- `projects` - User projects with slug
- `repositories` - Git repository metadata
- `repository_connections` - Provider-specific connections with SSH keys
- `workspaces` - Project workspaces
- `tool_definitions` - Manifest-driven tool definitions
- `tool_instances` - Running/spawned tool instances
- `configs` - Key-value config storage (scoped)
- `secrets` - Encrypted secret storage (scoped)
- `access_routes` - Traefik routing rules
## API Endpoints
### Implemented Routers
- `/api/v1/users` - User management
- `/api/v1/projects` - Project CRUD
- `/api/v1/repositories` - Repository management
- `/api/v1/workspaces` - Workspace management
- `/api/v1/tool-definitions` - Tool registry CRUD
- `/api/v1/tool-instances` - Tool instance lifecycle
- `/api/v1/configs` - Config management
- `/api/v1/secrets` - Secret management
- `/api/v1/access-routes` - Routing rules
- `/api/v1/tools` - Tool registry (manifest-driven)
- `/health` - Health check
## Open Questions (from mvp-scope.md)
1. **Admin role in MVP:** Do we need a basic admin role for global config management?
2. **User slug derivation:** Display name, email local-part, or dedicated slug column?
3. **Provider adapter coverage:** Which Git providers get concrete adapters in MVP?
4. **Auto-deploy-key registration:** Automatic via provider APIs or manual copy-paste?
5. **Container image trust:** Allow-list or any image reference?
6. **Billing or resource quotas:** Usage limiting needed in MVP?
## File Structure
```
headquarter/
├── apps/
│ ├── web/ # React frontend (skeleton)
│ └── api/ # FastAPI backend (models + routers)
├── docs/ # Architecture, scope, development docs
├── deploy/ # Portainer/Traefik deployment examples
├── openspec/ # Spec-driven workflow
│ ├── config.yaml # Project context for AI
│ ├── changes/ # Active changes
│ │ ├── frontend-foundation/ # FN-005
│ │ ├── deployment-config/ # FN-006
│ │ ├── config-secrets/ # FN-009
│ │ ├── codeserver-spawn/ # FN-010
│ │ └── opencode-poc/ # FN-008
│ └── specs/ # Project specsheets
│ └── project-specsheet.md
├── docker-compose.yml # Local development stack
├── docker-compose.traefik.yml
├── Makefile # Common workflows
└── package.json # Root monorepo scripts
```
## Test Status
- **Frontend:** Vitest configured, basic App.test.tsx passing
- **Backend:** pytest configured, tests for git provider, credentials, operations
- **CI:** GitHub Actions runs on PR/push to main
## Definition of MVP Done
1. ✅ Monorepo scaffold complete
2. ✅ Architecture documented
3. ✅ Backend models and migrations
4. ✅ Tool registry with manifests
5. ✅ Git provider abstraction
6. 📋 Frontend auth and navigation (spec ready)
7. 📋 Config/secrets UI and runtime injection (spec ready)
8. 📋 code-server spawn flow (spec ready)
9. 📋 OpenCode terminal environment (spec ready)
10. 📋 Production deployment stack (spec ready)
11. ⏳ All tests passing
12. ⏳ Documentation consistent with implementation
+65
View File
@@ -0,0 +1,65 @@
# SSH Key Management Specification
## Purpose
Generate and manage SSH keys for git operations with external providers.
## Requirements
### Requirement: Key Generation
The system SHALL generate Ed25519 SSH key pairs.
#### Scenario: Generate key
- GIVEN an authenticated user
- WHEN they request a new SSH key
- THEN an Ed25519 key pair is generated
- AND the private key is encrypted with Fernet
- AND the public key is stored in OpenSSH format
### Requirement: Key Association
The system SHALL support user-level and project-level keys.
#### Scenario: User-level key
- GIVEN an authenticated user
- WHEN they generate a key without specifying a project
- THEN it's associated with their user account
#### Scenario: Project-level key
- GIVEN an authenticated user with a project
- WHEN they generate a key for that project
- THEN it's associated with the project
### Requirement: Key Display
The system SHALL display public keys for copying.
#### Scenario: Copy public key
- GIVEN an authenticated user
- WHEN they view their SSH keys
- THEN each public key is displayed in OpenSSH format
- AND a copy button is available
### Requirement: Key Deletion
The system SHALL support key removal.
#### Scenario: Delete key
- GIVEN an authenticated user
- WHEN they delete an SSH key
- THEN it's removed from the database
- AND the key files are deleted
## Dependencies
- Database models: SSHKey, User, Project
- cryptography library for key generation
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
+90
View File
@@ -0,0 +1,90 @@
# Tool Instance Management Specification
## Purpose
Launch, monitor, and manage development tool instances in Docker containers.
## Requirements
### Requirement: Tool Instance Creation
The system SHALL create and launch tool instances from repositories.
#### Scenario: Launch tool
- GIVEN an authenticated user with a project and repository
- WHEN they create a tool instance
- THEN:
1. A unique subdomain is generated: `{tool-name}-{tool-id}.hq.local`
2. The Docker Compose template is rendered with project values
3. `docker compose up -d` is executed
4. Container ID and status are stored
### Requirement: Tool Lifecycle
The system SHALL manage tool lifecycle operations.
#### Scenario: Stop tool
- GIVEN a running tool instance
- WHEN the user stops it
- THEN `docker compose stop` is executed
- AND status is updated to "stopped"
#### Scenario: Start tool
- GIVEN a stopped tool instance
- WHEN the user starts it
- THEN `docker compose start` is executed
- AND status is updated to "running"
#### Scenario: Delete tool
- GIVEN a tool instance
- WHEN the user deletes it
- THEN the container and volumes are removed
- AND the database record is deleted
### Requirement: Traefik Integration
The system SHALL auto-generate Traefik labels for routing.
#### Scenario: Route generation
- GIVEN a running tool instance
- THEN these labels are set:
- `traefik.enable=true`
- `traefik.http.routers.{tool_id}.rule=Host(\`{subdomain}.hq.local\`)`
- `traefik.http.routers.{tool_id}.entrypoints=web`
- `traefik.http.services.{tool_id}.loadbalancer.server.port={port}`
### Requirement: Status Monitoring
The system SHALL track tool status.
#### Scenario: Status check
- GIVEN a tool instance
- WHEN status is queried
- THEN the real-time container status is returned:
- pending, building, running, stopped, error
### Requirement: Log Access
The system SHALL provide access to container logs.
#### Scenario: View logs
- GIVEN a tool instance
- WHEN logs are requested
- THEN the last 100 lines are returned
- AND live streaming is available via WebSocket
## Dependencies
- tool-types (tool definitions)
- git-repo (repository access)
- project-management (project context)
- Docker runtime
- Traefik reverse proxy
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
+76
View File
@@ -0,0 +1,76 @@
# Web Terminal Specification
## Purpose
Provide browser-based terminal access to running tool containers.
## Requirements
### Requirement: WebSocket Terminal
The system SHALL provide terminal sessions via WebSocket.
#### Scenario: Open terminal
- GIVEN a running tool instance
- WHEN the user opens the terminal
- THEN a WebSocket connection is established
- AND a shell is spawned in the container via `docker exec`
### Requirement: Terminal I/O
The system SHALL stream terminal I/O via WebSocket.
#### Scenario: Command execution
- GIVEN an active terminal session
- WHEN the user types a command
- THEN stdin is forwarded to the container shell
- AND stdout/stderr is streamed back to the browser
### Requirement: Terminal Resize
The system SHALL support terminal resize events.
#### Scenario: Resize terminal
- GIVEN an active terminal session
- WHEN the browser window is resized
- THEN the terminal dimensions (COLS, ROWS) are updated
- AND the shell receives the new size
### Requirement: Session Management
The system SHALL manage terminal sessions.
#### Scenario: Multiple sessions
- GIVEN a running tool instance
- WHEN multiple terminals are opened
- THEN each has an independent session
#### Scenario: Cleanup
- GIVEN an active terminal session
- WHEN the user disconnects
- THEN the session is cleaned up
- AND the shell process is terminated
### Requirement: Access Control
The system SHALL restrict terminal access.
#### Scenario: Unauthorized access
- GIVEN a tool instance owned by user A
- WHEN user B tries to access the terminal
- THEN the connection is rejected with 403
## Dependencies
- tool-instances (running containers)
- auth-oauth (authentication)
- xterm.js frontend library
- ptyprocess for pseudo-TTY
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
+67
View File
@@ -0,0 +1,67 @@
# Tool Type Definition Specification
## Purpose
Define and register tool types using Docker Compose templates for launching development tools.
## Requirements
### Requirement: Tool Type Model
The system SHALL store tool type definitions in the database.
#### Scenario: Create tool type
- GIVEN an admin user
- WHEN they define a new tool type
- THEN the following fields are stored:
- name: Tool identifier
- description: Human-readable description
- docker_compose_template: Compose file template
- icon: Visual identifier
- category: Tool category
- default_env_vars: Default environment variables
- default_ports: Exposed ports
### Requirement: Template Variables
The system SHALL support template variable substitution.
#### Scenario: Variable substitution
- GIVEN a Docker Compose template
- WHEN it's rendered for a tool instance
- THEN these variables are substituted:
- `{{REPO_PATH}}`: Path to the git repository
- `{{WORKSPACE_DIR}}`: Working directory inside container
- `{{USER_ID}}`: User identifier
- `{{PROJECT_ID}}`: Project identifier
- `{{TOOL_ID}}`: Tool instance identifier
### Requirement: Built-in Tools
The system SHALL include default tool types.
#### Scenario: Built-in tools
- GIVEN a fresh installation
- THEN these tool types are pre-configured:
- code-server: VS Code in browser
- jupyter-notebook: Jupyter notebooks
- opencode: OpenCode agent environment
### Requirement: Template Validation
The system SHALL validate Docker Compose templates.
#### Scenario: Invalid template
- GIVEN an invalid Docker Compose template
- WHEN a user tries to create/update a tool type
- THEN the system rejects with validation errors
## Dependencies
- Database models: ToolType
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
+69
View File
@@ -0,0 +1,69 @@
# User Configuration Specification
## Purpose
Store and manage user preferences and settings.
## Requirements
### Requirement: Key-Value Storage
The system SHALL store user configuration as JSONB key-value pairs.
#### Scenario: Store preferences
- GIVEN an authenticated user
- WHEN they update their settings
- THEN the configuration is stored in the UserConfig model
### Requirement: Supported Config Keys
The system SHALL support specific configuration keys.
#### Scenario: Supported keys
- GIVEN the configuration system
- THEN these keys SHALL be supported:
- `default_editor`: Preferred code editor
- `theme`: UI theme preference
- `git_user_name`: Git commit author name
- `git_user_email`: Git commit author email
### Requirement: Config Retrieval
The system SHALL return user configuration.
#### Scenario: Get config
- GIVEN an authenticated user
- WHEN they access settings
- THEN their current configuration is returned
### Requirement: Config Updates
The system SHALL support partial configuration updates.
#### Scenario: Update single key
- GIVEN an authenticated user with existing config
- WHEN they update just the theme
- THEN only that key is modified
- AND other keys remain unchanged
### Requirement: Frontend Integration
The system SHALL apply configuration in the frontend.
#### Scenario: Apply theme
- GIVEN a user with theme preference set
- WHEN they load the application
- THEN the selected theme is applied
## Dependencies
- Database models: UserConfig, User
- auth-oauth (authenticated users)
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass
+50
View File
@@ -0,0 +1,50 @@
# User Profile Management Specification
## Purpose
Manage user profiles including personal information and avatar.
## Requirements
### Requirement: Profile Retrieval
The system SHALL allow users to view their profile.
#### Scenario: View profile
- GIVEN an authenticated user
- WHEN they access the profile page
- THEN their name, email, and avatar are displayed
### Requirement: Profile Updates
The system SHALL allow users to update their profile.
#### Scenario: Update name and email
- GIVEN an authenticated user
- WHEN they submit profile changes
- THEN the system validates the input
- AND updates the user record
### Requirement: Avatar Upload
The system SHALL support local avatar storage.
#### Scenario: Upload avatar
- GIVEN an authenticated user
- WHEN they upload an image file
- THEN the system validates the file type and size
- AND stores it locally
- AND updates the user's avatar URL
## Dependencies
- auth-oauth (authenticated users)
- Database models: User
## Quality Gates
- `pytest` must pass
- `mypy .` must pass
- `ruff check .` must pass
- `npm run typecheck` must pass
- `npm run lint` must pass