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