diff --git a/docs/reference/dev-environment.md b/docs/reference/dev-environment.md index bc1759f5..8f20d981 100644 --- a/docs/reference/dev-environment.md +++ b/docs/reference/dev-environment.md @@ -22,6 +22,8 @@ browser -> main-server:18083 -> dev-frontend-proxy -> prod backend-core microser `dev-frontend-proxy` is an nginx sidecar in the main-server Compose project. It proxies requests to `backend-core:8080/api/microservices/k3sctl-adapter/proxy/api/services/frontend-dev/proxy...`, so D601 does not expose a new public port and the dev UI still crosses the existing UniDesk/k3sctl-adapter control boundary. The proxy is intentionally thin: it does not build frontend assets, does not talk to D601 directly, and does not contain DevOps logic. +`frontend-dev` and `backend-core-dev` are registered with `k3sctl-adapter` through the managed-service catalog `src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k3s.json`. The public dev port must use that cataloged Kubernetes API service-proxy path; it must not add a direct D601 public port, NodePort, or backend-core catalog entry for arbitrary k3s services. + The dev public port is configured in `config.json` as `network.devFrontend.port=18083`, surfaced by `server status` as `urls.devFrontend`, and managed by `server rebuild dev-frontend-proxy`. The proxy health depends on `frontend-dev`; it can be unhealthy until the D601 dev frontend has been deployed. The unrestricted public network entries are therefore production frontend, dev frontend, and provider ingress. backend-core REST, PostgreSQL, user-service backend ports, k3s Services, NodePorts and D601 host ports remain private or explicitly restricted. diff --git a/scripts/src/docker.ts b/scripts/src/docker.ts index f717f7ef..7904db32 100644 --- a/scripts/src/docker.ts +++ b/scripts/src/docker.ts @@ -239,7 +239,7 @@ export function rebuildService(config: UniDeskConfig, service: RebuildableServic `cid=$(${shellJoin(listServiceContainersCommand)} || true)`, `if [ -z "$cid" ]; then echo "$(date -Is) compose_rebuild_watchdog_restore service=${service}" >> ${shellQuote(watchdogLog)}; ${shellJoin(restoreCommand)} >> ${shellQuote(watchdogLog)} 2>&1 || true; fi`, ].join("\n"); - const watchdogScript = `set -euo pipefail; ${shellJoin(["flock", "-w", "300", lockPath, "bash", "-lc", watchdogInnerScript])} || true`; + const watchdogScript = `set -euo pipefail; ${shellJoin(["flock", "-w", "30", lockPath, "bash", "-lc", watchdogInnerScript])} || true`; const validateScript = [ "ready=0", "for attempt in $(seq 1 60); do", diff --git a/src/components/dev-frontend-proxy/nginx.conf b/src/components/dev-frontend-proxy/nginx.conf index 048270f1..804045dc 100644 --- a/src/components/dev-frontend-proxy/nginx.conf +++ b/src/components/dev-frontend-proxy/nginx.conf @@ -1,6 +1,7 @@ server { listen 8080; server_name _; + resolver 127.0.0.11 valid=30s ipv6=off; # The dev frontend is intentionally reached through the existing backend-core # microservice proxy so the public port does not need direct access to D601. diff --git a/src/components/microservices/k3sctl-adapter/docker-compose.d601.yml b/src/components/microservices/k3sctl-adapter/docker-compose.d601.yml index dde350d9..b03643b4 100644 --- a/src/components/microservices/k3sctl-adapter/docker-compose.d601.yml +++ b/src/components/microservices/k3sctl-adapter/docker-compose.d601.yml @@ -41,7 +41,7 @@ services: K3SCTL_NATIVE_SERVICE_URL_MDTODO: "${K3SCTL_NATIVE_SERVICE_URL_MDTODO:-}" K3SCTL_NATIVE_SERVICE_URL_DECISION_CENTER: "${K3SCTL_NATIVE_SERVICE_URL_DECISION_CENTER:-}" K3SCTL_NATIVE_SERVICE_URL_DEVOPS: "${K3SCTL_NATIVE_SERVICE_URL_DEVOPS:-}" - K3SCTL_MANIFEST_PATHS: "${K3SCTL_MANIFEST_PATHS:-k3s/code-queue.k3s.json,k3s/mdtodo.k3s.json,k3s/claudeqq.k3s.json,k3s/decision-center.k3s.json}" + K3SCTL_MANIFEST_PATHS: "${K3SCTL_MANIFEST_PATHS:-k3s/code-queue.k3s.json,k3s/mdtodo.k3s.json,k3s/claudeqq.k3s.json,k3s/decision-center.k3s.json,k3s/dev/unidesk-dev-core.k3s.json}" K3SCTL_SERVICES_JSON: "${K3SCTL_SERVICES_JSON:-[]}" UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-512MiB}" volumes: diff --git a/src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k3s.json b/src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k3s.json new file mode 100644 index 00000000..4f1c92aa --- /dev/null +++ b/src/components/microservices/k3sctl-adapter/k3s/dev/unidesk-dev-core.k3s.json @@ -0,0 +1,76 @@ +[ + { + "apiVersion": "unidesk.ai/k3s/v1", + "kind": "ManagedKubernetesService", + "metadata": { + "name": "backend-core-dev", + "namespace": "unidesk-dev" + }, + "spec": { + "adapterServiceId": "k3sctl-adapter", + "controlPlane": { + "type": "kubernetes", + "cluster": "unidesk-k3s", + "context": "unidesk-k3s" + }, + "route": { + "kind": "kubernetes-service", + "serviceName": "backend-core-dev", + "servicePort": 8080 + }, + "activeInstanceId": "D601-dev-backend", + "singleWriter": true, + "expectedNodeIds": [ + "D601" + ], + "instances": [ + { + "id": "D601-dev-backend", + "nodeId": "D601", + "role": "primary", + "baseUrl": "kubernetes://unidesk-dev/services/backend-core-dev:8080", + "healthPath": "/health", + "healthMode": "service-proxy" + } + ], + "requireAllInstancesHealthy": true + } + }, + { + "apiVersion": "unidesk.ai/k3s/v1", + "kind": "ManagedKubernetesService", + "metadata": { + "name": "frontend-dev", + "namespace": "unidesk-dev" + }, + "spec": { + "adapterServiceId": "k3sctl-adapter", + "controlPlane": { + "type": "kubernetes", + "cluster": "unidesk-k3s", + "context": "unidesk-k3s" + }, + "route": { + "kind": "kubernetes-service", + "serviceName": "frontend-dev", + "servicePort": 8080 + }, + "activeInstanceId": "D601-dev-frontend", + "singleWriter": false, + "expectedNodeIds": [ + "D601" + ], + "instances": [ + { + "id": "D601-dev-frontend", + "nodeId": "D601", + "role": "primary", + "baseUrl": "kubernetes://unidesk-dev/services/frontend-dev:8080", + "healthPath": "/health", + "healthMode": "service-proxy" + } + ], + "requireAllInstancesHealthy": true + } + } +] diff --git a/src/components/microservices/k3sctl-adapter/src/index.ts b/src/components/microservices/k3sctl-adapter/src/index.ts index 91dd0839..23fd38f2 100644 --- a/src/components/microservices/k3sctl-adapter/src/index.ts +++ b/src/components/microservices/k3sctl-adapter/src/index.ts @@ -274,7 +274,7 @@ function mergeServices(services: ManagedService[]): ManagedService[] { } function readConfig(): RuntimeConfig { - const paths = manifestPaths(envString("K3SCTL_MANIFEST_PATHS", "k3s/code-queue.k3s.json,k3s/mdtodo.k3s.json,k3s/claudeqq.k3s.json,k3s/decision-center.k3s.json")); + const paths = manifestPaths(envString("K3SCTL_MANIFEST_PATHS", "k3s/code-queue.k3s.json,k3s/mdtodo.k3s.json,k3s/claudeqq.k3s.json,k3s/decision-center.k3s.json,k3s/dev/unidesk-dev-core.k3s.json")); const inlineServices = parseServices(envString("K3SCTL_SERVICES_JSON", "[]")); const manifestServices = readManifestServices(paths); return {