docs: update all documentation for OpenCode and deployment

- Update architecture.md with spawn service and auth proxy sections
- Update deployment.md with production stack details
- Update development.md with spawn workflow documentation
- Update mvp-scope.md, project-brief.md, tool-manifest-spec.md
- Update conversation-handoff.md with current status
- Replace all RunFusion references with OpenCode
This commit is contained in:
2026-05-14 17:30:03 +02:00
parent 139654d5c0
commit 62640daf36
7 changed files with 263 additions and 86 deletions
+66 -20
View File
@@ -5,11 +5,11 @@
## 1. Overview & Goals
Headquarter is a hosted control plane where authenticated users create projects, connect Git repositories, and spawn containerized tools (RunFusion, code-server, and future tools). The platform is manifest-driven and provider-abstracted so new tools, Git providers, runtimes, and access providers can be added without rewriting core orchestration logic.
Headquarter is a hosted control plane where authenticated users create projects, connect Git repositories, and spawn containerized tools (OpenCode, code-server, and future tools). The platform is manifest-driven and provider-abstracted so new tools, Git providers, runtimes, and access providers can be added without rewriting core orchestration logic.
**Product purpose:** Give individual developers and small teams a self-hosted alternative to cloud IDEs and CI dashboards by combining Git-backed project workspaces with on-demand tool containers, all routed through a unified subdomain scheme.
**MVP scope:** Single-user projects, Authentik OIDC auth, Docker runtime, Traefik subdomain routing, Portainer-managed Docker Compose deployment. The MVP supports two built-in tools (RunFusion and code-server) and provides extension points for additional tools, Git providers, and runtimes.
**MVP scope:** Single-user projects, Authentik OIDC auth, Docker runtime, Traefik subdomain routing, Portainer-managed Docker Compose deployment. The MVP supports two built-in tools (OpenCode and code-server) and provides extension points for additional tools, Git providers, and runtimes.
**Non-goals (explicitly out of MVP scope):**
- Multi-user teams or shared projects
@@ -43,7 +43,7 @@ flowchart TB
subgraph Runtime
R[Docker Compose Stack<br/>Portainer-managed]
TC[Tool Containers<br/>RunFusion / code-server]
TC[Tool Containers<br/>OpenCode / code-server]
end
U -->|HTTPS| T
@@ -81,27 +81,54 @@ flowchart TB
- Consume the FastAPI backend via a typed API client layer
- Handle Vite environment variables for runtime configuration
**Technology Stack:**
- React 19 + TypeScript with strict mode
- Vite for build tooling
- React Router v7 for client-side routing
- TanStack Query v5 for server state management
- Zustand for client state management (auth store)
- Tailwind CSS v4 for styling
- Headless UI for accessible components
- Heroicons for iconography
**Project Structure:**
```
apps/web/src/
├── api/ # API client and error handling
├── auth/ # OIDC utilities and AuthProvider
├── components/ # Reusable UI components (Header, Sidebar, RouteGuard, DashboardLayout)
├── pages/ # Page components (Dashboard, Projects, Tools, Settings, etc.)
├── stores/ # Zustand stores (auth store)
├── types/ # TypeScript type definitions matching backend schemas
└── router.tsx # React Router configuration
```
**Routing:**
- `/` — Dashboard
- `/projects` — Project list
- `/projects/new` — Create project
- `/projects/:id/repositories` — Repository management
- `/tools` — Tool registry and spawn surface
- `/tools/spawn` — Spawn tool form
- `/settings` — User and platform settings
- `/access/:instanceId` — Tool access URL presentation
- `/` — Dashboard (protected)
- `/projects` — Project list (protected)
- `/projects/new` — Create project (protected)
- `/projects/:id` — Project detail (protected)
- `/projects/:id/edit` — Edit project (protected)
- `/repositories` — Repository management (protected)
- `/tools` — Tool registry and spawn surface (protected)
- `/settings` — User and platform settings (protected)
- `/login` — Login redirect (public)
- `/callback` — OIDC callback handler (public)
**Auth state:**
- Managed via a central auth context/provider
- Managed via Zustand auth store (`useAuthStore`)
- States: `loading`, `authenticated`, `unauthenticated`, `error`
- Access token stored in **httpOnly cookie** (recommended) or secure storage; never `localStorage` for sensitive tokens
- On 401 from API, redirect to Authentik login
- Access token stored in `localStorage` (MVP simplification; httpOnly cookie recommended for production)
- PKCE flow for OIDC authentication
- On 401 from API, redirect to login
**API client conventions:**
- Base URL from `VITE_API_BASE_URL`
- Base URL from `VITE_API_URL`
- JSON request/response with standard HTTP status codes
- Normalize errors into a consistent `{ message, statusCode, details? }` shape
- Include bearer token or cookie credentials on every request
- `ApiError` class for consistent error handling
- Bearer token injected via `Authorization` header
- Debug mode request/response logging
- Typed API methods for all endpoints
### 3.2 Backend (`apps/api/`)
@@ -233,7 +260,7 @@ All tables use `uuid` primary keys. All models include `created_at` and `updated
| Column | Type | Constraints | Default | Notes |
|--------|------|-------------|---------|-------|
| `id` | `UUID` | `PRIMARY KEY` | `uuid_generate_v4()` | |
| `key` | `VARCHAR(100)` | `UNIQUE`, `NOT NULL`, `INDEX` | | Machine identifier (e.g., `runfusion`, `code-server`) |
| `key` | `VARCHAR(100)` | `UNIQUE`, `NOT NULL`, `INDEX` | | Machine identifier (e.g., `opencode`, `code-server`) |
| `name` | `VARCHAR(255)` | `NOT NULL` | | Human-readable name |
| `version` | `VARCHAR(50)` | `NOT NULL` | `'1.0.0'` | |
| `description` | `TEXT` | `NULLABLE` | `NULL` | |
@@ -449,7 +476,7 @@ Tools are defined by manifests that declare runtime behavior, resource needs, an
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `key` | `str` | Yes | Machine identifier (`runfusion`, `code-server`) |
| `key` | `str` | Yes | Machine identifier (`opencode`, `code-server`) |
| `name` | `str` | Yes | Human-readable name |
| `version` | `str` | No | SemVer string; default `1.0.0` |
| `description` | `str` | No | Markdown-friendly description |
@@ -730,7 +757,7 @@ Variable interpolation rules:
Example:
```
https://runfusion-myapp-alice.tools.example.com
https://opencode-myapp-alice.tools.example.com
https://code-server-myapp-alice.tools.example.com
```
@@ -780,6 +807,25 @@ Tool containers **must** attach to the external Traefik Docker network. If the n
- **TLS is enabled by default** for all tool instances.
- HTTP-only mode (`web` entrypoint, no TLS) is supported for local development via environment configuration.
### 10.6 Auth Proxy for Tool Instances
Spawned tools are protected behind the platform's authentication via Traefik forwardAuth middleware:
1. **Middleware Configuration:**
- Traefik forwards incoming requests to `/api/v1/auth/validate`
- The endpoint validates the Bearer token and returns 200 for authenticated users
- Unauthenticated requests receive 401 and are blocked
2. **Tool Configuration:**
- code-server built-in auth is disabled (`PASSWORD: ""`)
- OpenCode relies entirely on the platform auth layer
- Tools run on internal networks only, inaccessible directly
3. **Security Model:**
- Only platform-authenticated users can access spawned tools
- Each tool instance has its own subdomain with isolated routing
- No shared containers between users or projects
---
## 11. Storage Layout
+1 -1
View File
@@ -62,7 +62,7 @@ This document captures the key architectural decisions, assumptions, and open lo
- **FN-004:** Backend Foundation — depends on the PostgreSQL domain model in Section 4
- **FN-005:** Frontend Foundation — depends on component boundaries in Section 3.1
- **FN-006:** Deployment Config — depends on Traefik routing model in Section 10
- **FN-008:** RunFusion POC — depends on spawn lifecycle in Section 8 and Docker runtime in Section 9
- **FN-008:** OpenCode POC — depends on spawn lifecycle in Section 8 and Docker runtime in Section 9
- **FN-009:** Config & Secrets — depends on storage layout in Section 11 and security in Section 13
- **FN-010:** code-server Spawn — depends on tool manifest in Section 7 and Docker runtime in Section 9
- **FN-011:** Git Provider — depends on Git provider abstraction in Section 5
+109 -38
View File
@@ -2,61 +2,132 @@
## Overview
The MVP deployment target is a **Portainer-managed Docker Compose stack** with an existing **Traefik** reverse proxy.
The MVP deployment target is a **Portainer-managed Docker Compose stack** with **Traefik** as the reverse proxy. All services run in Docker containers with automatic TLS via Let's Encrypt.
This document covers the scaffold-level deployment assumptions created in FN-002. Detailed deployment automation (dynamic labels for spawned tool containers, secret rotation, CI/CD pipelines) is follow-up scope for **FN-006**.
## Architecture
## Stack Assumptions
```
Internet
|
v
Traefik (443/80) ──► Let's Encrypt TLS
|
├──► api.example.com ──► FastAPI backend
├──► example.com ──► React frontend
├──► traefik.example.com ──► Traefik dashboard
└──► {tool}-{project}-{user}.tools.example.com ──► Spawned tool containers
```
- **Reverse proxy**: Traefik (already running on the target host)
- **Orchestration**: Portainer managing Docker Compose stacks
- **Network**: External Traefik network named `traefik` (or as configured)
- **Routing**: Subdomain-based (`{tool}-{project}-{user}.tools.{ROOT_DOMAIN}`)
- **TLS**: Traefik cert resolver (e.g., `letsencrypt` or Cloudflare)
## Prerequisites
- Docker and Docker Compose
- A domain with DNS A/AAAA records pointing to your server
- Ports 80 and 443 open
## Quick Start
### 1. Configure Environment
Copy the production environment example and fill in all values:
```bash
cp deploy/.env.example deploy/.env
```
Required variables:
| Variable | Description | Example |
|----------|-------------|---------|
| `ROOT_DOMAIN` | Your domain | `example.com` |
| `ACME_EMAIL` | Let's Encrypt contact email | `admin@example.com` |
| `POSTGRES_PASSWORD` | Database password | (strong random) |
| `AUTHENTIK_CLIENT_SECRET` | OIDC client secret | (from Authentik) |
| `SECRET_ENCRYPTION_KEY` | Fernet encryption key | (32-byte base64) |
### 2. Deploy Locally (Testing)
```bash
docker compose -f docker-compose.prod.yml up --build -d
```
This starts: Traefik, API, web frontend, and PostgreSQL.
### 3. Deploy to Production (Portainer)
1. In Portainer, create a new stack
2. Upload `deploy/portainer-stack.yml`
3. Set environment variables from `deploy/portainer.env.example`
4. Deploy the stack
## Deployment Files
The following deployment files are part of the scaffold:
| File | Purpose |
|------|---------|
| `docker-compose.yml` | Local development (API, web, Postgres) |
| `docker-compose.traefik.yml` | Deployment overlay with Traefik labels |
| `deploy/portainer.env.example` | Deployment environment variables |
| `deploy/traefik-labels.example.yml` | Example Traefik labels for services |
| `deploy/README.md` | Deploy skeleton usage notes |
| `docker-compose.prod.yml` | Production compose with Traefik |
| `deploy/portainer-stack.yml` | Portainer stack definition |
| `deploy/portainer.env.example` | Portainer environment variables |
| `deploy/.env.example` | Production environment variables |
## Environment Variables
## Traefik Configuration
See `.env.example` for the full variable list. Key deployment variables:
Traefik handles all routing and TLS:
```env
APP_NAME=Headquarter
ROOT_DOMAIN=example.com
TOOL_DOMAIN=tools.example.com
TRAEFIK_NETWORK=traefik
TRAEFIK_ENTRYPOINT=websecure
TRAEFIK_CERT_RESOLVER=letsencrypt
AUTHENTIK_ISSUER_URL=https://auth.example.com/application/o/headquarter/
AUTHENTIK_CLIENT_ID=
AUTHENTIK_CLIENT_SECRET=
POSTGRES_PASSWORD=
SECRET_ENCRYPTION_KEY=
- **Entrypoints**: `web` (80) → redirects to `websecure` (443)
- **Certificates**: Let's Encrypt via TLS challenge
- **Dashboard**: Available at `traefik.${ROOT_DOMAIN}` (protected by middleware)
- **Metrics**: Prometheus metrics exposed on `/metrics`
### Subdomain Routing
Tool containers are routed via subdomains:
```
{tool}-{project}-{user}.tools.{ROOT_DOMAIN}
```
## Local vs Production
Example: `vscode-myproject-john.tools.example.com`
- **Local**: `docker compose up --build -d` uses `docker-compose.yml` only.
- **Production**: Portainer deploys the stack using the main compose file plus the Traefik overlay.
## DNS Requirements
## Scoped Secrets
Create DNS A records for:
Do not commit real secrets. Use:
- `example.com` → your server IP
- `*.example.com` → your server IP (wildcard for subdomains)
- `*.tools.example.com` → your server IP (tool subdomains)
- Portainer environment variables (stored in Portainer, not in Git)
- `.env` files (ignored by Git, documented in `.env.example`)
- Docker secrets (to be evaluated in FN-006)
## Security
## Follow-up Work
- All services communicate over HTTPS
- Traefik adds security headers (HSTS, XSS protection, etc.)
- Database is not exposed externally
- Secrets are injected via environment variables
- **FN-006**: Full deployment automation, dynamic Traefik labels for spawned tool containers, Portainer stack definitions, and CI/CD integration.
## Updating
To update the deployment:
```bash
# Pull new images
docker compose -f docker-compose.prod.yml pull
# Restart services
docker compose -f docker-compose.prod.yml up -d
```
## Troubleshooting
Check Traefik logs:
```bash
docker logs traefik
```
Check service health:
```bash
docker compose -f docker-compose.prod.yml ps
```
Verify certificates:
```bash
curl -v https://api.example.com
```
+64
View File
@@ -31,6 +31,29 @@ Copy the frontend environment example:
cp apps/web/.env.example apps/web/.env
```
### Frontend Authentication (OIDC)
The frontend uses OpenID Connect (OIDC) with PKCE for authentication. Configure the following environment variables in `apps/web/.env`:
| Variable | Description | Example |
|----------|-------------|---------|
| `VITE_API_URL` | Backend API base URL | `http://localhost:8000` |
| `VITE_OIDC_ISSUER` | OIDC provider issuer URL | `https://authentik.example.com/application/o/headquarter` |
| `VITE_OIDC_CLIENT_ID` | OIDC client ID | `headquarter-web` |
| `VITE_OIDC_REDIRECT_URI` | Post-login redirect URL | `http://localhost:5173/callback` |
**Authentication Flow:**
1. User clicks login → redirected to OIDC provider authorize endpoint
2. User authenticates with provider
3. Provider redirects to `/callback` with authorization code
4. Frontend exchanges code for access token (PKCE)
5. Token stored in `localStorage`, user info fetched from `/api/v1/users/me`
**Logout:**
- Clears local token
- Redirects to login page
- User can re-authenticate via OIDC flow
## Running Locally
### Frontend only
@@ -195,3 +218,44 @@ Security rules for the package:
- Credential models store **only** `encrypted_payload` — no plaintext `token` or `private_key` fields.
- SSH private keys are encrypted before storage; the field uses `repr=False`.
- Real encryption of the payload is deferred to FN-009; the current placeholder is base64-only.
## Tool Spawn Workflow
The platform supports spawning development tools (e.g., code-server) as Docker containers via Docker Compose.
### Architecture
1. **Tool Manifest** (`apps/api/app/tools/manifests/*.yml`):
- Defines Docker image, ports, volumes, environment variables, health checks
- Loaded into in-memory registry at application startup
2. **Spawn Service** (`apps/api/app/services/spawn.py`):
- Generates Docker Compose service definitions from manifests
- Handles container lifecycle: spawn, stop, status polling
- Integrates Traefik label generation for subdomain routing
3. **API Endpoints** (`apps/api/app/routers/tool_instances.py`):
- `POST /projects/{id}/tool-instances` — Spawn a new tool instance
- `POST /projects/{id}/tool-instances/{id}/stop` — Stop a running instance
- `POST /projects/{id}/tool-instances/{id}/start` — Restart a stopped instance
- `GET /projects/{id}/tool-instances/{id}/status` — Get container status
4. **Frontend UI**:
- `/tools/spawn` — Form to select tool, project, and spawn
- `/projects/{id}/instances/{id}` — Instance detail with status, controls, and "Open Tool" link
### Auth Proxy
Spawned tools are protected behind Traefik forwardAuth middleware:
- Traefik forwards requests to `/api/v1/auth/validate` for session validation
- code-server built-in auth is disabled (`PASSWORD: ""`)
- Only authenticated platform users can access spawned tools
### Local Development
Ensure Docker socket is accessible and the `tools` network exists:
```bash
docker network create tools # One-time setup
```
Spawned containers use the `tools` network for Traefik routing.
+6 -6
View File
@@ -7,7 +7,7 @@
## 1. Product Vision
Headquarter is a hosted workspace and tool-orchestration platform for developers who want self-hosted control over their development environments. It gives authenticated users a single dashboard to create Git-backed projects, connect repositories from any provider, and spawn containerized tools—starting with RunFusion and code-server—on demand, each accessible via its own HTTPS subdomain. Headquarter is for individual developers and small teams who outgrow cloud IDEs but do not want to build their own orchestration layer from scratch.
Headquarter is a hosted workspace and tool-orchestration platform for developers who want self-hosted control over their development environments. It gives authenticated users a single dashboard to create Git-backed projects, connect repositories from any provider, and spawn containerized tools—starting with OpenCode and code-server—on demand, each accessible via its own HTTPS subdomain. Headquarter is for individual developers and small teams who outgrow cloud IDEs but do not want to build their own orchestration layer from scratch.
---
@@ -36,7 +36,7 @@ An MVP user can complete the following end-to-end flows without assistance:
- The public key is displayed to the user for manual registration at the provider, or registered automatically via the provider adapter when available.
### 2.5 Spawn a Tool Instance
- User navigates to "Tools" and selects a tool (RunFusion or code-server).
- User navigates to "Tools" and selects a tool (OpenCode or code-server).
- User chooses a project and optional config overrides.
- The backend generates a Docker Compose service definition, Traefik labels, and starts the container.
- The tool instance receives workspace mounts, config mounts, and secret injection.
@@ -70,7 +70,7 @@ An MVP user can complete the following end-to-end flows without assistance:
- **Project management** (CRUD, ownership-based)
- **Repository connections** with provider-agnostic Git URL storage
- **Per-repository SSH key generation** (Ed25519) with encrypted private-key storage
- **Tool registry** with manifest-driven definitions for RunFusion and code-server
- **Tool registry** with manifest-driven definitions for OpenCode and code-server
- **Tool instance spawning** via Docker Compose with Traefik subdomain routing
- **Tool instance lifecycle** (start, stop, health checks, status tracking)
- **Persistent config storage** at global, user, project, and tool-instance scopes
@@ -111,12 +111,12 @@ Slices are ordered by dependency. Each slice corresponds to a task on the Fusion
| 2 | **FN-019** | Architecture & Specification | Enhanced `docs/architecture.md`, `docs/mvp-scope.md`, doc validation tests |
| 3 | **FN-004** | Backend Foundation | Domain models, Alembic migrations, auth boundaries, secret encryption, API routers |
| 4 | **FN-005** | Frontend Foundation | Auth shell, navigation, placeholder pages, API client, config layer |
| 5 | **FN-003** | Tool Registry | Manifest schema, in-memory registry, built-in RunFusion/code-server manifests, FastAPI routes |
| 5 | **FN-003** | Tool Registry | Manifest schema, in-memory registry, built-in OpenCode/code-server manifests, FastAPI routes |
| 6 | **FN-006** | Deployment Config | Traefik label generator, production Compose stacks, Portainer stack definition |
| 7 | **FN-011** | Git Provider Model | Provider abstraction, SSH key lifecycle, credential models, repository connection |
| 8 | **FN-009** | Config & Secrets | Encrypted storage, runtime injection, frontend config/secrets UI |
| 9 | **FN-010** | code-server Spawn | code-server manifest, spawn flow, runtime integration, auth layer |
| 10 | **FN-008** | RunFusion POC | Executable environment, Node/npm runtime, health reporting |
| 10 | **FN-008** | OpenCode POC | AI-powered terminal environment, web interface, health reporting |
**Dependency notes:**
- FN-004 and FN-005 can proceed in parallel once FN-019 is complete.
@@ -139,7 +139,7 @@ FN-002 (Scaffold)
│ ├──> FN-003 (Tool Registry)
│ │ │
│ │ ├──> FN-010 (code-server Spawn)
│ │ └──> FN-008 (RunFusion POC)
│ │ └──> FN-008 (OpenCode POC)
│ │
│ ├──> FN-011 (Git Provider)
│ │
+1 -1
View File
@@ -2,7 +2,7 @@
## What
Headquarter is a hosted workspace and tool-orchestration platform. Authenticated users create Git-backed projects and launch containerized development tools—starting with RunFusion and code-server—each exposed via its own HTTPS subdomain.
Headquarter is a hosted workspace and tool-orchestration platform. Authenticated users create Git-backed projects and launch containerized development tools—starting with OpenCode and code-server—each exposed via its own HTTPS subdomain.
## Why
+16 -20
View File
@@ -5,7 +5,7 @@
## Overview
Headquarter is a manifest-driven platform: every containerized tool (RunFusion, code-server, and future tools) is declared by a YAML manifest. The orchestration backend reads these manifests to generate Docker Compose services, Traefik routing labels, volume mounts, and resource constraints.
Headquarter is a manifest-driven platform: every containerized tool (OpenCode, code-server, and future tools) is declared by a YAML manifest. The orchestration backend reads these manifests to generate Docker Compose services, Traefik routing labels, volume mounts, and resource constraints.
**Design goal:** Adding a new standard container tool requires only a YAML manifest—no backend code changes.
@@ -135,17 +135,17 @@ traefik:
7. `memory_mb`, if set, must be ≥ 16.
8. If `traefik.enabled` is `true`, at least one port must have `primary: true`.
## Example: RunFusion Manifest
## Example: OpenCode Manifest
```yaml
id: runfusion
name: RunFusion
description: Executable Node.js environment for running and developing applications.
id: opencode
name: OpenCode
description: AI-powered terminal-based development environment with web interface.
version: "1.0.0"
image: node:22-slim
image: ghcr.io/opencode-ai/opencode:latest
runtime_working_dir: /workspace
ports:
- container_port: 8080
- container_port: 3000
protocol: tcp
name: http
primary: true
@@ -156,32 +156,28 @@ workspace_mounts:
read_only: false
config_mounts:
- type: volume
source_pattern: "{user_config}/runfusion"
target: /home/node/.config
source_pattern: "{user_config}/opencode"
target: /root/.config/opencode
read_only: false
env:
NODE_ENV: development
TERM: xterm-256color
FORCE_COLOR: "1"
health_check:
type: http
path: /
port: 8080
port: 3000
interval_seconds: 10
timeout_seconds: 5
retries: 3
start_period_seconds: 10
start_period_seconds: 15
resource_limits:
cpus: 2.0
memory_mb: 2048
memory_mb: 4096
memory_swap_mb: -1
executable:
node_version: "22"
package_manager: npm
bootstrap_commands: []
install_commands: []
traefik:
enabled: true
subdomain_prefix: runfusion
port: 8080
subdomain_prefix: opencode
port: 3000
middlewares: []
strip_prefix: false
```