mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
feat(deploy): docker-compose + Helm chart entry slice (Phase 5)
Plan J4 / spec §15.4 / §15.5 / §16 Phase 5.
Three landings:
- deploy/Dockerfile now COPYs plugins/_official/ into the image so
the bundled atom plugins from §3.I3 register on container boot —
without this, registerBundledPlugins() silently no-ops inside the
container and the §23 self-bootstrap promise breaks for hosted
deployments.
- tools/pack/docker-compose.yml ships the canonical hosted-mode
manifest spec §15.4 calls out: two-volume layout (od-data +
od-config) per §15.2, OD_BIND_HOST=0.0.0.0 + OD_API_TOKEN +
OD_NAMESPACE + snapshot retention knobs as env, /api/daemon/status
as the healthcheck endpoint (Phase 1.5). Drop-in usable with
`docker compose -f tools/pack/docker-compose.yml up -d`.
- tools/pack/helm/open-design/{Chart.yaml,values.yaml,README.md}
pins the Helm chart parameter surface for the per-cloud overrides
spec §15.5 enumerates (AWS / GCP / Azure / Aliyun / Tencent /
Huawei / self-hosted). Templates land in the Phase 5 follow-up
PR; the values schema is locked here so the per-cloud override
files (values-<cloud>.yaml) review in isolation.
scripts/guard.ts allowlist gains
`packages/agui-adapter/esbuild.config.mjs` so the new package
passes the residual-JS guard.
Daemon tests stay at 1486/1486 (deploy artifacts only).
Co-authored-by: Tom Huang <1043269994@qq.com>
This commit is contained in:
co-authored by
Tom Huang
parent
847304ebc5
commit
bf30b308e3
@@ -81,6 +81,12 @@ COPY --chown=open-design:open-design craft ./craft
|
||||
COPY --chown=open-design:open-design prompt-templates ./prompt-templates
|
||||
COPY --chown=open-design:open-design assets/frames ./assets/frames
|
||||
COPY --chown=open-design:open-design assets/community-pets ./assets/community-pets
|
||||
# Plan §3.J4 / spec §23.3.5 — bundled atom plugins registered on
|
||||
# daemon boot. The directory contains `plugins/_official/atoms/<atom>/`
|
||||
# pairs (SKILL.md + open-design.json); registerBundledPlugins() walks
|
||||
# it on startup so the container ships with first-party atoms reachable
|
||||
# via the same registry path third-party plugins use.
|
||||
COPY --chown=open-design:open-design plugins/_official ./plugins/_official
|
||||
|
||||
RUN mkdir -p /app/.od && \
|
||||
chown -R open-design:open-design /app
|
||||
|
||||
@@ -36,6 +36,7 @@ const residualSkippedDirectories = new Set([
|
||||
const residualAllowedExactPaths = new Set([
|
||||
// esbuild config entrypoints are executed directly by Node before package
|
||||
// dist output exists.
|
||||
"packages/agui-adapter/esbuild.config.mjs",
|
||||
"packages/contracts/esbuild.config.mjs",
|
||||
"packages/platform/esbuild.config.mjs",
|
||||
"packages/plugin-runtime/esbuild.config.mjs",
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Phase 5 / spec §15.4 — reference Docker Compose manifest.
|
||||
#
|
||||
# Brings up the Open Design daemon container alongside the persistent
|
||||
# volumes and the env vars hosted deployments need. Drop this file into
|
||||
# any directory and run `docker compose up -d` — the only required
|
||||
# customisation is the OD_API_TOKEN secret and the provider keys.
|
||||
#
|
||||
# Build the image first (from the repo root):
|
||||
#
|
||||
# docker build -f deploy/Dockerfile -t open-design/od:dev .
|
||||
#
|
||||
# Or pull a published tag:
|
||||
#
|
||||
# docker pull ghcr.io/open-design/od:edge
|
||||
#
|
||||
# Then `docker compose -f tools/pack/docker-compose.yml up -d`.
|
||||
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
od:
|
||||
# The image tag the operator wants. Spec §15.5 expects multi-arch
|
||||
# ghcr.io/open-design/od:<tag>; the build path above produces
|
||||
# `open-design/od:dev` locally.
|
||||
image: ${OD_IMAGE:-ghcr.io/open-design/od:edge}
|
||||
container_name: od
|
||||
|
||||
# Spec §15.3 — every dimension the daemon respects in headless
|
||||
# mode. OD_BIND_HOST=0.0.0.0 + OD_API_TOKEN is the canonical
|
||||
# hosted-mode tuple; remove these two if running on a private
|
||||
# docker network with an upstream reverse proxy that already
|
||||
# gates access.
|
||||
environment:
|
||||
OD_BIND_HOST: ${OD_BIND_HOST:-0.0.0.0}
|
||||
OD_PORT: ${OD_PORT:-7456}
|
||||
OD_DATA_DIR: /data/od
|
||||
OD_MEDIA_CONFIG_DIR: /data/config
|
||||
# Set to a generated random hex (`openssl rand -hex 32`) before
|
||||
# exposing to the public internet. Phase 5 wires the daemon to
|
||||
# refuse OD_BIND_HOST=0.0.0.0 without OD_API_TOKEN.
|
||||
OD_API_TOKEN: ${OD_API_TOKEN:-}
|
||||
OD_NAMESPACE: ${OD_NAMESPACE:-production}
|
||||
# Snapshot retention knobs (spec §11.4 PB2). Default ttl is 30
|
||||
# days; set to 0 to keep snapshots forever.
|
||||
OD_SNAPSHOT_UNREFERENCED_TTL_DAYS: ${OD_SNAPSHOT_UNREFERENCED_TTL_DAYS:-30}
|
||||
OD_SNAPSHOT_GC_INTERVAL_MS: ${OD_SNAPSHOT_GC_INTERVAL_MS:-21600000}
|
||||
# Provider credentials. Leave blank to require a UI / CLI
|
||||
# configure step inside the running container.
|
||||
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
||||
TAVILY_API_KEY: ${TAVILY_API_KEY:-}
|
||||
|
||||
# Two-volume layout per spec §15.2 — mounting /data/config
|
||||
# separately lets the operator rotate provider credentials on a
|
||||
# different lifecycle than the project data.
|
||||
volumes:
|
||||
- od-data:/data/od
|
||||
- od-config:/data/config
|
||||
|
||||
ports:
|
||||
- "${OD_PORT:-7456}:${OD_PORT:-7456}"
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
healthcheck:
|
||||
# /api/daemon/status is the canonical lifecycle probe (Phase 1.5).
|
||||
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:${OD_PORT:-7456}/api/daemon/status >/dev/null 2>&1 || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
od-data: {}
|
||||
od-config: {}
|
||||
@@ -0,0 +1,23 @@
|
||||
apiVersion: v2
|
||||
name: open-design
|
||||
description: |
|
||||
Open Design daemon Helm chart. Spec §15.5 reference deployment for
|
||||
AWS / GCP / Azure / Aliyun / Tencent / Huawei / self-hosted Kubernetes.
|
||||
Pairs with `tools/pack/docker-compose.yml` as the docker-only entry
|
||||
point.
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.5.0"
|
||||
icon: https://open-design.ai/favicon.png
|
||||
keywords:
|
||||
- open-design
|
||||
- design-agent
|
||||
- generative-ui
|
||||
- plugins
|
||||
- skills
|
||||
home: https://open-design.ai
|
||||
sources:
|
||||
- https://github.com/nexu-io/open-design
|
||||
maintainers:
|
||||
- name: Open Design
|
||||
url: https://open-design.ai
|
||||
@@ -0,0 +1,39 @@
|
||||
# Open Design Helm chart
|
||||
|
||||
> Spec §15.5 reference deployment.
|
||||
> Status: **values surface locked, templates pending.**
|
||||
|
||||
This chart is the parameter contract Phase 5 standardised on. The
|
||||
`templates/` directory is intentionally absent in this slice — the
|
||||
follow-up PR that lands the Kubernetes manifests will consume the
|
||||
same `values.yaml` schema.
|
||||
|
||||
## Why a placeholder
|
||||
|
||||
The plan §6 Phase 5 deliverable is a **single** Helm chart with values
|
||||
presets per cloud. Splitting that into "values surface now, templates
|
||||
later" keeps the cloud-specific overrides reviewable in isolation
|
||||
(every cloud's volume + secret pattern shows up as a `values-<cloud>.yaml`
|
||||
override file).
|
||||
|
||||
## Cloud overrides (planned values files)
|
||||
|
||||
```text
|
||||
values-aws.yaml persistence.backend=efs secrets.backend=secrets-manager
|
||||
values-gcp.yaml persistence.backend=filestore secrets.backend=secret-manager
|
||||
values-azure.yaml persistence.backend=files secrets.backend=key-vault
|
||||
values-aliyun.yaml persistence.backend=nas secrets.backend=kms
|
||||
values-tencent.yaml persistence.backend=cfs secrets.backend=ssm
|
||||
values-huawei.yaml persistence.backend=sfs secrets.backend=kms
|
||||
values-self.yaml persistence.backend=hostPath secrets.backend=env
|
||||
```
|
||||
|
||||
## Installing once the templates land
|
||||
|
||||
```bash
|
||||
helm repo add open-design https://open-design.ai/charts
|
||||
helm install od open-design/open-design \
|
||||
--set image.tag=edge \
|
||||
--set secrets.apiToken="$(openssl rand -hex 32)" \
|
||||
-f values-aws.yaml # one of the cloud overrides above
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
# Open Design daemon — Helm chart values. Spec §15.5.
|
||||
#
|
||||
# Override the image / persistence / secret pattern per cloud:
|
||||
# AWS: persistence.backend=efs secrets.backend=secrets-manager
|
||||
# GCP: persistence.backend=filestore + gcsfuse secrets.backend=secret-manager
|
||||
# Azure: persistence.backend=files secrets.backend=key-vault
|
||||
# Aliyun: persistence.backend=nas secrets.backend=kms
|
||||
# Tencent: persistence.backend=cfs secrets.backend=ssm
|
||||
# Huawei: persistence.backend=sfs secrets.backend=kms
|
||||
# Self-hosted: persistence.backend=hostPath / nfs / longhorn
|
||||
#
|
||||
# These values are reference defaults; the chart's templates/ aren't
|
||||
# wired here yet (Phase 5 follow-up). The file pins the parameter
|
||||
# surface so a downstream PR can implement the templates without
|
||||
# revisiting the deployment shape.
|
||||
|
||||
image:
|
||||
repository: ghcr.io/open-design/od
|
||||
tag: edge
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
# `replicaCount: 1` is the v1 default — pluggable storage + Postgres
|
||||
# adapter (spec §15.6) lift this in a follow-up.
|
||||
replicaCount: 1
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 7456
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
data:
|
||||
size: 20Gi
|
||||
storageClass: ""
|
||||
config:
|
||||
size: 1Gi
|
||||
storageClass: ""
|
||||
|
||||
env:
|
||||
OD_BIND_HOST: 0.0.0.0
|
||||
OD_PORT: "7456"
|
||||
OD_DATA_DIR: /data/od
|
||||
OD_MEDIA_CONFIG_DIR: /data/config
|
||||
OD_NAMESPACE: production
|
||||
OD_SNAPSHOT_UNREFERENCED_TTL_DAYS: "30"
|
||||
OD_SNAPSHOT_GC_INTERVAL_MS: "21600000"
|
||||
|
||||
# Secrets are NOT inlined in values.yaml — operators inject them
|
||||
# through their cloud's secret manager. This block is the schema the
|
||||
# templates will consume.
|
||||
secrets:
|
||||
apiToken: "" # OD_API_TOKEN
|
||||
anthropicApiKey: "" # ANTHROPIC_API_KEY
|
||||
tavilyApiKey: "" # TAVILY_API_KEY
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 2
|
||||
memory: 2Gi
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
|
||||
podSecurityContext:
|
||||
runAsUser: 1001
|
||||
runAsGroup: 1001
|
||||
fsGroup: 1001
|
||||
runAsNonRoot: true
|
||||
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
readOnlyRootFilesystem: false
|
||||
|
||||
ingress:
|
||||
enabled: false
|
||||
className: ""
|
||||
annotations: {}
|
||||
hosts: []
|
||||
tls: []
|
||||
|
||||
nodeSelector: {}
|
||||
tolerations: []
|
||||
affinity: {}
|
||||
Reference in New Issue
Block a user