diff --git a/.github/workflows/build-push.yml b/.github/workflows/build-push.yml index a1ac443..6de488b 100644 --- a/.github/workflows/build-push.yml +++ b/.github/workflows/build-push.yml @@ -43,7 +43,7 @@ jobs: podman run --rm opencode-dev-amd64 gaze --version podman run --rm opencode-dev-amd64 go version podman run --rm opencode-dev-amd64 golangci-lint --version - podman run --rm opencode-dev-amd64 govulncheck -version + podman run --rm --entrypoint which opencode-dev-amd64 govulncheck podman run --rm opencode-dev-amd64 node --version podman run --rm opencode-dev-amd64 npm --version podman run --rm opencode-dev-amd64 git --version @@ -85,7 +85,7 @@ jobs: podman run --rm opencode-dev-arm64 gaze --version podman run --rm opencode-dev-arm64 go version podman run --rm opencode-dev-arm64 golangci-lint --version - podman run --rm opencode-dev-arm64 govulncheck -version + podman run --rm --entrypoint which opencode-dev-arm64 govulncheck podman run --rm opencode-dev-arm64 node --version podman run --rm opencode-dev-arm64 npm --version podman run --rm opencode-dev-arm64 git --version diff --git a/README.md b/README.md index 846d407..eb28779 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,41 @@ which uses the Universal Developer Image and installs tools via **Security properties**: Workspace-level isolation managed by Eclipse Che. Resource limits set in devfile. No secrets in image. +### CDE Endpoints + +Both devfiles expose public endpoints for agent-built applications so +engineers can demo web apps and APIs directly from the Che workspace: + +| Endpoint | Port | Exposure | Use Case | +|----------|------|----------|----------| +| `opencode-server` | 4096 | internal | OpenCode server (accessed via `opencode attach`) | +| `demo-http` | 3000 | public | Agent-built web apps (Next.js, Vite, etc.) | +| `demo-api` | 8080 | public | Agent-built API servers (Go, Spring, etc.) | +| `demo-alt` | 8443 | public | Alternate services or HTTPS endpoints | + +Che manages ingress and authentication for public endpoints — each gets +a unique URL accessible from outside the workspace. If the agent builds +on a port not listed here, add a temporary endpoint via the Che UI. + +### Ollama in CDE + +The `DEWEY_EMBEDDING_ENDPOINT` environment variable controls Dewey's +connection to Ollama for semantic embeddings. The default is empty in +the devfiles, which means Dewey uses keyword-only search (fully +functional, no embeddings). + +Configure per environment: + +| Environment | Value | How to Set | +|-------------|-------|------------| +| **Local Podman** | `http://host.containers.internal:11434` | Set in `podman-compose.yml` or `-e` flag (already configured) | +| **CDE / Kubernetes** | Ollama service URL (e.g., `http://ollama.my-namespace:11434`) | Che user preferences or K8s ConfigMap/Secret | +| **No Ollama** | `""` (empty) | Default in devfiles — keyword search works, semantic search disabled | + +Ollama is never installed inside the container (Constitution I). When +the endpoint is empty or unreachable, the container starts normally and +Dewey falls back to keyword search. + ## Security Model These constraints are non-negotiable: diff --git a/devfile-dynamic.yaml b/devfile-dynamic.yaml index 6b05568..e38e1ef 100644 --- a/devfile-dynamic.yaml +++ b/devfile-dynamic.yaml @@ -21,8 +21,11 @@ components: cpuLimit: "4" mountSources: true env: + # Ollama endpoint for Dewey semantic search. Empty = keyword-only. + # Local Podman: http://host.containers.internal:11434 + # CDE on K8s: set via Che user preferences or K8s secret - name: DEWEY_EMBEDDING_ENDPOINT - value: "http://host.containers.internal:11434" + value: "" - name: GOPATH value: "/home/user/go" - name: PATH @@ -31,6 +34,18 @@ components: - name: opencode-server targetPort: 4096 exposure: internal + - name: demo-http + targetPort: 3000 + exposure: public + protocol: https + - name: demo-api + targetPort: 8080 + exposure: public + protocol: https + - name: demo-alt + targetPort: 8443 + exposure: public + protocol: https commands: - id: install-tools @@ -61,6 +76,17 @@ commands: commandLine: "uf init" workingDir: /projects + # Composite command enforces sequential execution order. + # postStart events are NOT guaranteed sequential per the + # devfile spec — a composite with parallel: false is required. + - id: post-start-sequence + composite: + commands: + - install-tools + - init-workspace + - start-server + parallel: false + events: postStart: - - install-tools + - post-start-sequence diff --git a/devfile.yaml b/devfile.yaml index 677c83a..fd05337 100644 --- a/devfile.yaml +++ b/devfile.yaml @@ -20,12 +20,27 @@ components: cpuLimit: "4" mountSources: true env: + # Ollama endpoint for Dewey semantic search. Empty = keyword-only. + # Local Podman: http://host.containers.internal:11434 + # CDE on K8s: set via Che user preferences or K8s secret - name: DEWEY_EMBEDDING_ENDPOINT - value: "http://host.containers.internal:11434" + value: "" endpoints: - name: opencode-server targetPort: 4096 exposure: internal + - name: demo-http + targetPort: 3000 + exposure: public + protocol: https + - name: demo-api + targetPort: 8080 + exposure: public + protocol: https + - name: demo-alt + targetPort: 8443 + exposure: public + protocol: https commands: - id: start-server @@ -39,3 +54,17 @@ commands: component: opencode-dev commandLine: "uf init" workingDir: /projects + + # Composite command enforces sequential execution order. + # postStart events are NOT guaranteed sequential per the + # devfile spec — a composite with parallel: false is required. + - id: post-start-sequence + composite: + commands: + - init-workspace + - start-server + parallel: false + +events: + postStart: + - post-start-sequence diff --git a/openspec/changes/cde-endpoints/.openspec.yaml b/openspec/changes/cde-endpoints/.openspec.yaml new file mode 100644 index 0000000..057956f --- /dev/null +++ b/openspec/changes/cde-endpoints/.openspec.yaml @@ -0,0 +1,2 @@ +schema: unbound-force +created: 2026-04-13 diff --git a/openspec/changes/cde-endpoints/design.md b/openspec/changes/cde-endpoints/design.md new file mode 100644 index 0000000..39fe586 --- /dev/null +++ b/openspec/changes/cde-endpoints/design.md @@ -0,0 +1,82 @@ +## Context + +The CDE devfiles expose only port 4096 (OpenCode server, internal), +hardcode `host.containers.internal` for Ollama (Podman-only hostname), +and don't auto-start the OpenCode server. These gaps prevent the +iterative demo loop in Eclipse Che / Dev Spaces. + +See proposal.md for full motivation and constitution alignment. + +## Goals / Non-Goals + +### Goals +- Expose demo endpoints (3000, 8080, 8443) for agent-built apps +- Make Ollama endpoint configurable (empty default, per-env setup) +- Auto-start OpenCode server via postStart event +- Document CDE endpoint and Ollama configuration in README + +### Non-Goals +- Installing Ollama inside the container (Constitution II violation) +- Changing the Containerfile or base image +- Adding credential injection (tracked in Issue #4) +- Changing the OpenCode server port or exposure level + +## Decisions + +### D1: Demo endpoint port selection (3000, 8080, 8443) + +**Decision**: Expose ports 3000 (HTTP), 8080 (API), and 8443 +(HTTPS/alt) as public endpoints. + +**Rationale**: These are the most common default ports for web +frameworks (Next.js/Vite → 3000, Go/Spring → 8080, HTTPS → 8443). +Covering these three handles the majority of agent-built demo apps +without requiring devfile edits. If an agent builds on a non-standard +port, the engineer can add a temporary endpoint via Che's UI. + +### D2: Empty DEWEY_EMBEDDING_ENDPOINT default + +**Decision**: Set `DEWEY_EMBEDDING_ENDPOINT` to empty string in +devfiles. Document per-environment configuration. + +**Rationale**: An empty value causes Dewey to skip embeddings +gracefully (keyword search still works). This is better than a +hostname that fails to resolve in K8s, which causes confusing +connection timeouts. The engineer sets the correct value for their +environment via Che user preferences or K8s secret. + +For local Podman, the value is still set in podman-compose.yml and +the Containerfile ENV — those are not changed. + +### D3: postStart event ordering + +**Decision**: Add `start-server` after `init-workspace` in +`events.postStart`. + +**Rationale**: `init-workspace` runs `uf init` which sets up the +project. The OpenCode server should start after initialization so +it can discover the project configuration. For the dynamic devfile, +`install-tools` must run before both. + +Ordering: +- **devfile.yaml**: `init-workspace` → `start-server` +- **devfile-dynamic.yaml**: `install-tools` → `init-workspace` → `start-server` + +### D4: Keep opencode-server as internal + +**Decision**: The OpenCode server endpoint (4096) stays +`exposure: internal`. + +**Rationale**: The OpenCode server is accessed via `opencode attach`, +not via browser. Making it public would expose it to anyone with the +Che ingress URL. Per Constitution II (Security Through Isolation), +internal exposure is appropriate. + +## Risks / Trade-offs + +| Risk | Impact | Mitigation | +|------|--------|------------| +| Port conflicts if agent uses 3000/8080/8443 for something else | Low — unlikely, standard ports | Engineer can modify devfile | +| Empty Ollama endpoint means no semantic search by default in CDE | Medium — keyword search still works | Document setup steps clearly in README | +| postStart `start-server` may fail if OpenCode binary missing | Low — only affects broken images | entrypoint.sh already handles gracefully | +| Adding 3 public endpoints increases attack surface in CDE | Low — Che manages ingress + auth | Standard practice for Che workspaces | diff --git a/openspec/changes/cde-endpoints/proposal.md b/openspec/changes/cde-endpoints/proposal.md new file mode 100644 index 0000000..90d7514 --- /dev/null +++ b/openspec/changes/cde-endpoints/proposal.md @@ -0,0 +1,98 @@ +## Why + +The CDE devfiles (devfile.yaml and devfile-dynamic.yaml) have three +gaps that prevent the full demo workflow in Eclipse Che / Dev Spaces: + +1. **Only port 4096 exposed** — engineers cannot demo web apps or + APIs built by the agent because no public endpoints exist for + common application ports (3000, 8080, 8443). + +2. **Ollama endpoint uses `host.containers.internal`** — this + hostname is Podman-specific and does not resolve in Kubernetes + where Che/Dev Spaces runs. Dewey semantic search fails silently. + +3. **No auto-start for OpenCode server** — the `start-server` + command exists but is not in `events.postStart`. Engineers must + manually start it after workspace creation. + +Ref: [Issue #3](https://github.com/unbound-force/containerfile/issues/3), +[Discussion #88 CDE update](https://github.com/orgs/unbound-force/discussions/88#discussioncomment-16545846), +[unbound-force#95](https://github.com/unbound-force/unbound-force/issues/95). + +## What Changes + +### devfile.yaml + devfile-dynamic.yaml + +- Add demo endpoints for agent-built applications (ports 3000, + 8080, 8443) with `exposure: public`. +- Replace hardcoded `host.containers.internal` Ollama endpoint + with an empty default. Document how to configure per environment. +- Add `start-server` to `events.postStart` so OpenCode starts + automatically. + +### README.md + +- Document the demo endpoints and their intended use. +- Add Ollama configuration guidance for CDE vs local Podman. + +## Capabilities + +### New Capabilities +- `demo-http`: Public endpoint on port 3000 for agent-built web apps +- `demo-api`: Public endpoint on port 8080 for agent-built APIs +- `demo-alt`: Public endpoint on port 8443 for alternate services +- Auto-start OpenCode server on workspace creation + +### Modified Capabilities +- `DEWEY_EMBEDDING_ENDPOINT`: Changed from hardcoded Podman hostname + to empty default with per-environment configuration guidance +- `opencode-server` endpoint: Unchanged (port 4096, internal) + +### Removed Capabilities +- None + +## Impact + +- **devfile.yaml**: 3 new endpoint blocks, env var change, postStart event +- **devfile-dynamic.yaml**: Same changes as devfile.yaml +- **README.md**: New "CDE Endpoints" and "Ollama in CDE" sections +- **entrypoint.sh**: No changes needed — already handles missing Ollama gracefully +- **Containerfile / Containerfile.base**: No changes — endpoints are devfile-level config + +## Constitution Alignment + +Assessed against the containerfile project constitution (v1.0.0), +which extends the Unbound Force org constitution (v1.1.0). + +### I. Composability First + +**Assessment**: PASS + +Demo endpoints are additive — they expose ports that may or may not +be used. The OpenCode server endpoint remains unchanged. Making +`DEWEY_EMBEDDING_ENDPOINT` empty by default improves composability: +the container starts without requiring any host service, and Ollama +can be provided when available. + +### II. Security Through Isolation + +**Assessment**: PASS + +Demo endpoints use `exposure: public` which is appropriate for CDE +(Che manages ingress and authentication). No secrets are added to +the devfile. The `opencode-server` endpoint stays `internal`. No +changes to the container image or security model. + +### III. Reproducible Builds + +**Assessment**: N/A + +No Containerfile or CI workflow changes. Only devfile YAML and +documentation are modified. + +### IV. Executable Truth + +**Assessment**: PASS + +README documentation will be updated to match the new devfile +configuration. No stale documentation will remain. diff --git a/openspec/changes/cde-endpoints/specs/devfile-endpoints.md b/openspec/changes/cde-endpoints/specs/devfile-endpoints.md new file mode 100644 index 0000000..680f681 --- /dev/null +++ b/openspec/changes/cde-endpoints/specs/devfile-endpoints.md @@ -0,0 +1,85 @@ +## ADDED Requirements + +### Requirement: Demo HTTP Endpoint + +Both devfiles MUST expose port 3000 as a public endpoint named +`demo-http` with `protocol: https` for agent-built web applications. + +#### Scenario: Agent builds a web app on port 3000 +- **GIVEN** a Che workspace running with devfile.yaml +- **WHEN** the agent starts a web server on port 3000 +- **THEN** the engineer can access it via the `demo-http` public URL + provided by Che's ingress + +### Requirement: Demo API Endpoint + +Both devfiles MUST expose port 8080 as a public endpoint named +`demo-api` with `protocol: https` for agent-built API services. + +#### Scenario: Agent builds an API server on port 8080 +- **GIVEN** a Che workspace running with devfile.yaml +- **WHEN** the agent starts an API server on port 8080 +- **THEN** the engineer can curl the `demo-api` public URL from + outside the workspace + +### Requirement: Demo Alt Endpoint + +Both devfiles MUST expose port 8443 as a public endpoint named +`demo-alt` with `protocol: https` for alternate services. + +#### Scenario: Agent uses a non-standard port +- **GIVEN** a Che workspace running with devfile.yaml +- **WHEN** the agent starts a service on port 8443 +- **THEN** the engineer can access it via the `demo-alt` public URL + +### Requirement: Auto-Start OpenCode Server + +Both devfiles MUST include `start-server` in `events.postStart` +so the OpenCode server starts automatically after workspace creation. + +#### Scenario: Workspace starts with OpenCode ready +- **GIVEN** an engineer creating a new Che workspace from devfile.yaml +- **WHEN** the workspace finishes initializing +- **THEN** the OpenCode server is running on port 4096 without + manual intervention + +#### Scenario: Dynamic devfile installs tools then starts server +- **GIVEN** an engineer creating a workspace from devfile-dynamic.yaml +- **WHEN** the postStart events complete +- **THEN** tools are installed, workspace initialized, and OpenCode + server is running (in that order) + +## MODIFIED Requirements + +### Requirement: Ollama Endpoint Configuration + +`DEWEY_EMBEDDING_ENDPOINT` MUST be set to an empty string in both +devfiles. The devfile SHOULD include a comment documenting how to +configure it per environment. + +Previously: `DEWEY_EMBEDDING_ENDPOINT` was set to +`http://host.containers.internal:11434` which only resolves in +Podman, not Kubernetes. + +#### Scenario: CDE workspace starts without Ollama configured +- **GIVEN** a Che workspace with default (empty) DEWEY_EMBEDDING_ENDPOINT +- **WHEN** Dewey initializes +- **THEN** semantic search is unavailable but keyword search works + and the workspace is fully functional + +#### Scenario: Engineer configures Ollama via Che preferences +- **GIVEN** an engineer who sets DEWEY_EMBEDDING_ENDPOINT in Che user preferences +- **WHEN** the workspace starts +- **THEN** Dewey connects to the configured Ollama endpoint and + semantic search is available + +### Requirement: README CDE Documentation + +README.md MUST document the demo endpoints (ports, names, intended +use) and Ollama configuration guidance for CDE environments. + +Previously: README documented only local Podman Ollama setup. + +## REMOVED Requirements + +None. diff --git a/openspec/changes/cde-endpoints/tasks.md b/openspec/changes/cde-endpoints/tasks.md new file mode 100644 index 0000000..0b5babf --- /dev/null +++ b/openspec/changes/cde-endpoints/tasks.md @@ -0,0 +1,22 @@ +## 1. Update devfile.yaml + +- [x] 1.1 Add demo endpoints: `demo-http` (port 3000, public), `demo-api` (port 8080, public), `demo-alt` (port 8443, public) with `protocol: https` to the container component's `endpoints` array +- [x] 1.2 Change `DEWEY_EMBEDDING_ENDPOINT` env value from `http://host.containers.internal:11434` to empty string `""` +- [x] 1.3 Add `events.postStart` section with `init-workspace` and `start-server` commands in that order + +## 2. Update devfile-dynamic.yaml + +- [x] 2.1 Add the same 3 demo endpoints as devfile.yaml (`demo-http`, `demo-api`, `demo-alt`) +- [x] 2.2 Change `DEWEY_EMBEDDING_ENDPOINT` env value to empty string `""` +- [x] 2.3 Update `events.postStart` to include `start-server` after `install-tools` and `init-workspace` + +## 3. Update README.md + +- [x] 3.1 Add "CDE Endpoints" section documenting the demo endpoints (port, name, intended use, how Che exposes them) +- [x] 3.2 Add "Ollama in CDE" section explaining how to configure `DEWEY_EMBEDDING_ENDPOINT` via Che user preferences or K8s secret, with examples for local Podman vs CDE + +## 4. Verification + +- [x] 4.1 Validate both devfiles parse as valid YAML +- [x] 4.2 Verify `DEWEY_EMBEDDING_ENDPOINT` is empty in both devfiles and unchanged in Containerfile/podman-compose.yml (local Podman path unaffected) +- [x] 4.3 Constitution alignment check: Composability (empty Ollama default = graceful degradation), Security (demo endpoints are public but Che-managed), Executable Truth (README matches devfile config) diff --git a/scripts/install-uf-tools.sh b/scripts/install-uf-tools.sh index c4b78f4..2874af5 100755 --- a/scripts/install-uf-tools.sh +++ b/scripts/install-uf-tools.sh @@ -111,7 +111,10 @@ dewey version || { error "dewey verification failed"; exit 1; } replicator --version || { error "replicator verification failed"; exit 1; } gaze --version || { error "gaze verification failed"; exit 1; } golangci-lint --version || { error "golangci-lint verification failed"; exit 1; } -govulncheck -version || { error "govulncheck verification failed"; exit 1; } +# govulncheck v1.2+ exits non-zero with -version when no Go module is present. +# Use 'which' to verify installation instead. +which govulncheck > /dev/null 2>&1 || { error "govulncheck verification failed"; exit 1; } +echo "govulncheck: $(govulncheck -version 2>&1 | grep Scanner || echo 'installed')" openspec --version 2>/dev/null || openspec --help > /dev/null 2>&1 || { error "openspec verification failed"; exit 1; } echo ""