From 8b4784f5edfe99de6db5fec32729485f2b4b9ae0 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Sat, 16 May 2026 11:33:37 +0200 Subject: [PATCH] chore(openspec): archive completed changes - Archive config-secrets (FN-009) - 31 tasks complete - Archive runfusion-poc (FN-008) - 25 tasks complete - Archive deployment-config (FN-006) - 27 tasks complete - All changes moved to openspec/changes/archive/ --- .../2026-05-14-config-secrets/.openspec.yaml | 2 + .../2026-05-14-config-secrets/design.md | 63 +++++++++++++++++ .../2026-05-14-config-secrets/proposal.md | 29 ++++++++ .../specs/config-management/spec.md | 37 ++++++++++ .../specs/runtime-injection/spec.md | 40 +++++++++++ .../specs/secret-management/spec.md | 37 ++++++++++ .../2026-05-14-config-secrets/tasks.md | 48 +++++++++++++ .../.openspec.yaml | 2 + .../2026-05-14-deployment-config/design.md | 67 +++++++++++++++++++ .../2026-05-14-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 +++++++ .../2026-05-14-deployment-config/tasks.md | 44 ++++++++++++ .../2026-05-14-runfusion-poc/.openspec.yaml | 2 + .../2026-05-14-runfusion-poc/design.md | 65 ++++++++++++++++++ .../2026-05-14-runfusion-poc/proposal.md | 28 ++++++++ .../specs/executable-environment/spec.md | 27 ++++++++ .../specs/health-reporting/spec.md | 22 ++++++ .../specs/runfusion-manifest/spec.md | 15 +++++ .../archive/2026-05-14-runfusion-poc/tasks.md | 42 ++++++++++++ 22 files changed, 698 insertions(+) create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/.openspec.yaml create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/design.md create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/proposal.md create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/specs/config-management/spec.md create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/specs/runtime-injection/spec.md create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/specs/secret-management/spec.md create mode 100644 openspec/changes/archive/2026-05-14-config-secrets/tasks.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/.openspec.yaml create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/design.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/proposal.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/specs/portainer-deployment/spec.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/specs/production-compose-stack/spec.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/specs/subdomain-routing/spec.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/specs/traefik-label-generator/spec.md create mode 100644 openspec/changes/archive/2026-05-14-deployment-config/tasks.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/.openspec.yaml create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/design.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/proposal.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/specs/executable-environment/spec.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/specs/health-reporting/spec.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/specs/runfusion-manifest/spec.md create mode 100644 openspec/changes/archive/2026-05-14-runfusion-poc/tasks.md diff --git a/openspec/changes/archive/2026-05-14-config-secrets/.openspec.yaml b/openspec/changes/archive/2026-05-14-config-secrets/.openspec.yaml new file mode 100644 index 0000000..66dd08a --- /dev/null +++ b/openspec/changes/archive/2026-05-14-config-secrets/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-14 diff --git a/openspec/changes/archive/2026-05-14-config-secrets/design.md b/openspec/changes/archive/2026-05-14-config-secrets/design.md new file mode 100644 index 0000000..19bb157 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-config-secrets/proposal.md b/openspec/changes/archive/2026-05-14-config-secrets/proposal.md new file mode 100644 index 0000000..8a9afe1 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-config-secrets/specs/config-management/spec.md b/openspec/changes/archive/2026-05-14-config-secrets/specs/config-management/spec.md new file mode 100644 index 0000000..6b05e64 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-config-secrets/specs/runtime-injection/spec.md b/openspec/changes/archive/2026-05-14-config-secrets/specs/runtime-injection/spec.md new file mode 100644 index 0000000..7b2de18 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-config-secrets/specs/secret-management/spec.md b/openspec/changes/archive/2026-05-14-config-secrets/specs/secret-management/spec.md new file mode 100644 index 0000000..f6dc3bf --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-config-secrets/tasks.md b/openspec/changes/archive/2026-05-14-config-secrets/tasks.md new file mode 100644 index 0000000..a5d5611 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-config-secrets/tasks.md @@ -0,0 +1,48 @@ +## 1. Backend Enhancements + +- [x] 1.1 Update Config model with scope validation methods +- [x] 1.2 Update Secret model with encryption verification +- [x] 1.3 Enhance configs router with scope filtering and hierarchy resolution +- [x] 1.4 Enhance secrets router with scope filtering and hierarchy resolution +- [x] 1.5 Create apps/api/app/services/runtime_injection.py for config/secret resolution +- [x] 1.6 Implement config file generation for container mounts +- [x] 1.7 Implement secret env var generation for container injection +- [x] 1.8 Add validation to fail spawn when referenced secrets are missing + +## 2. Frontend - Config Management + +- [x] 2.1 Create ConfigList component at /projects/:id/configs +- [x] 2.2 Implement ConfigForm for creating/updating configs +- [x] 2.3 Add scope selector (project/instance/global) to config form +- [x] 2.4 Implement config delete with confirmation +- [x] 2.5 Add JSON formatting for config values + +## 3. Frontend - Secret Management + +- [x] 3.1 Create SecretList component at /projects/:id/secrets +- [x] 3.2 Implement SecretForm for creating/updating secrets +- [x] 3.3 Add masked value display (never show decrypted) +- [x] 3.4 Implement secret delete with confirmation +- [x] 3.5 Add scope selector to secret form + +## 4. Runtime Integration + +- [x] 4.1 Integrate runtime injection into tool instance spawn endpoint +- [x] 4.2 Update Docker Compose generation to include config mounts +- [x] 4.3 Update Docker Compose generation to include secret env vars +- [x] 4.4 Test config/secret injection in local Docker environment + +## 5. Testing & Verification + +- [x] 5.1 Write backend tests for config scope resolution +- [x] 5.2 Write backend tests for secret encryption/decryption +- [x] 5.3 Write backend tests for runtime injection +- [x] 5.4 Write frontend tests for ConfigList and SecretList +- [x] 5.5 Run full test suite: `make test` +- [x] 5.6 Run linters: `make lint` + +## 6. Documentation + +- [x] 6.1 Update docs/development.md with config/secrets workflow +- [x] 6.2 Add config/secrets UI guide to docs/architecture.md +- [x] 6.3 Document scope hierarchy and resolution rules diff --git a/openspec/changes/archive/2026-05-14-deployment-config/.openspec.yaml b/openspec/changes/archive/2026-05-14-deployment-config/.openspec.yaml new file mode 100644 index 0000000..66dd08a --- /dev/null +++ b/openspec/changes/archive/2026-05-14-deployment-config/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-14 diff --git a/openspec/changes/archive/2026-05-14-deployment-config/design.md b/openspec/changes/archive/2026-05-14-deployment-config/design.md new file mode 100644 index 0000000..dce0dfd --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/proposal.md b/openspec/changes/archive/2026-05-14-deployment-config/proposal.md new file mode 100644 index 0000000..50d060e --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/specs/portainer-deployment/spec.md b/openspec/changes/archive/2026-05-14-deployment-config/specs/portainer-deployment/spec.md new file mode 100644 index 0000000..ae3ec1c --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/specs/production-compose-stack/spec.md b/openspec/changes/archive/2026-05-14-deployment-config/specs/production-compose-stack/spec.md new file mode 100644 index 0000000..29f8079 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/specs/subdomain-routing/spec.md b/openspec/changes/archive/2026-05-14-deployment-config/specs/subdomain-routing/spec.md new file mode 100644 index 0000000..9735e26 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/specs/traefik-label-generator/spec.md b/openspec/changes/archive/2026-05-14-deployment-config/specs/traefik-label-generator/spec.md new file mode 100644 index 0000000..c69803a --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-deployment-config/tasks.md b/openspec/changes/archive/2026-05-14-deployment-config/tasks.md new file mode 100644 index 0000000..8f66045 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/.openspec.yaml b/openspec/changes/archive/2026-05-14-runfusion-poc/.openspec.yaml new file mode 100644 index 0000000..66dd08a --- /dev/null +++ b/openspec/changes/archive/2026-05-14-runfusion-poc/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-14 diff --git a/openspec/changes/archive/2026-05-14-runfusion-poc/design.md b/openspec/changes/archive/2026-05-14-runfusion-poc/design.md new file mode 100644 index 0000000..97d16af --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/proposal.md b/openspec/changes/archive/2026-05-14-runfusion-poc/proposal.md new file mode 100644 index 0000000..753f95b --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/specs/executable-environment/spec.md b/openspec/changes/archive/2026-05-14-runfusion-poc/specs/executable-environment/spec.md new file mode 100644 index 0000000..e466f15 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/specs/health-reporting/spec.md b/openspec/changes/archive/2026-05-14-runfusion-poc/specs/health-reporting/spec.md new file mode 100644 index 0000000..c902a89 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/specs/runfusion-manifest/spec.md b/openspec/changes/archive/2026-05-14-runfusion-poc/specs/runfusion-manifest/spec.md new file mode 100644 index 0000000..168c526 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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/archive/2026-05-14-runfusion-poc/tasks.md b/openspec/changes/archive/2026-05-14-runfusion-poc/tasks.md new file mode 100644 index 0000000..1a74228 --- /dev/null +++ b/openspec/changes/archive/2026-05-14-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 + +- [x] 2.1 Verify OpenCode image availability and configuration +- [x] 2.2 Document web terminal access pattern +- [x] 2.3 Configure environment variables for terminal support +- [x] 2.4 Test container locally with docker run +- [x] 2.5 Verify web terminal accessibility + +## 3. Spawn Integration + +- [x] 3.1 Verify SpawnService (FN-010) can spawn OpenCode instances +- [x] 3.2 Add OpenCode-specific volume mounts (config) +- [x] 3.3 Test spawn via API endpoint +- [x] 3.4 Verify Traefik routing to OpenCode container + +## 4. Frontend Integration + +- [x] 4.1 Add OpenCode to tool selection dropdown +- [x] 4.2 Display OpenCode-specific options in spawn form +- [x] 4.3 Show OpenCode instance status in detail page + +## 5. Testing & Verification + +- [x] 5.1 Test terminal availability in spawned container +- [x] 5.2 Test web interface accessibility +- [x] 5.3 Test health endpoint response +- [x] 5.4 Verify workspace mount is accessible +- [x] 5.5 Run full test suite: `make test` +- [x] 5.6 Run linters: `make lint` + +## 6. Documentation + +- [x] 6.1 Document OpenCode setup in docs/development.md +- [x] 6.2 Add OpenCode usage guide +- [x] 6.3 Document terminal configuration and AI features