feat: add d601 dev environment foundation

This commit is contained in:
Codex
2026-05-17 17:33:36 +00:00
parent f0dc754103
commit 40d03621c5
8 changed files with 802 additions and 0 deletions
@@ -0,0 +1,568 @@
apiVersion: v1
kind: Namespace
metadata:
name: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
unidesk.ai/k3s-cluster: d601-native-k3s
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: unidesk-dev-quota
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
hard:
pods: "30"
requests.cpu: "4"
requests.memory: 8Gi
requests.storage: 30Gi
limits.cpu: "8"
limits.memory: 12Gi
persistentvolumeclaims: "8"
---
apiVersion: v1
kind: LimitRange
metadata:
name: unidesk-dev-default-limits
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
limits:
- type: Container
defaultRequest:
cpu: 50m
memory: 96Mi
default:
memory: 512Mi
max:
cpu: "2"
memory: 2Gi
---
apiVersion: v1
kind: Secret
metadata:
name: unidesk-dev-runtime-secrets
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
annotations:
unidesk.ai/template: "true"
unidesk.ai/description: "Dev-only placeholder secret; replace values on D601 before exposing dev services."
type: Opaque
stringData:
POSTGRES_USER: unidesk_dev
POSTGRES_PASSWORD: change-me-dev-postgres-password
POSTGRES_DB: unidesk_dev
DATABASE_URL: postgres://unidesk_dev:change-me-dev-postgres-password@postgres-dev.unidesk-dev.svc.cluster.local:5432/unidesk_dev
PROVIDER_TOKEN: change-me-D601-dev-provider-token
AUTH_USERNAME: admin-dev
AUTH_PASSWORD: change-me-dev-auth-password
SESSION_SECRET: change-me-dev-session-secret-minimum-32-characters
OPENAI_API_KEY: replace-me-openai-api-key
CRS_OAI_KEY: replace-me-crs-oai-key
MINIMAX_API_KEY: replace-me-minimax-api-key
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unidesk-dev-runtime-config
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
data:
UNIDESK_ENV: dev
UNIDESK_NAMESPACE: unidesk-dev
UNIDESK_DEPLOY_REF: origin/deploy/dev
UNIDESK_PROVIDER_ID: D601-dev
UNIDESK_NODE_ID: D601
UNIDESK_DEV_DATABASE_NAME: unidesk_dev
UNIDESK_DEV_DATABASE_ALLOWED_HOSTS: postgres-dev,postgres-dev.unidesk-dev,postgres-dev.unidesk-dev.svc,postgres-dev.unidesk-dev.svc.cluster.local
UNIDESK_DEV_DATABASE_FORBIDDEN_PATTERNS: d601-tcp-egress-gateway,database:5432/unidesk,74.48.78.17:15432
DATABASE_VOLUME_NAME: postgres-dev-data
DATABASE_VOLUME_SIZE: 5Gi
HEARTBEAT_TIMEOUT_MS: "30000"
TASK_PENDING_TIMEOUT_MS: "600000"
DATABASE_POOL_MAX: "2"
MICROSERVICES_JSON: "[]"
SESSION_TTL_SECONDS: "28800"
CODE_QUEUE_MAIN_PROVIDER_ID: D601-dev
CODE_QUEUE_EXECUTION_PROVIDER_IDS: D601-dev
CODE_QUEUE_WORKDIR: /workspace-dev
CODE_QUEUE_REMOTE_WORKDIR: /home/ubuntu/unidesk-dev-workspace
CODE_QUEUE_DATA_DIR: /var/lib/unidesk-dev/code-queue
CODE_QUEUE_CODEX_HOME: /var/lib/unidesk-dev/code-queue/codex-home
CODE_QUEUE_OPENCODE_XDG_DIR: /var/lib/unidesk-dev/code-queue/opencode-xdg
CODE_QUEUE_DEFAULT_MODEL: gpt-5.5
CODE_QUEUE_MODELS: gpt-5.5,gpt-5.4-mini,gpt-5.4,minimax-m2.7
CODE_QUEUE_MODEL_REASONING_EFFORTS: gpt-5.5=xhigh
CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED: "false"
CODE_QUEUE_STARTUP_OA_BACKFILL_ENABLED: "false"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unidesk-dev-db-guard
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
data:
guard-dev-db-url.sh: |
#!/bin/sh
set -eu
database_url="${1:-${DATABASE_URL:-}}"
expected_name="${UNIDESK_DEV_DATABASE_NAME:-unidesk_dev}"
if [ -z "$database_url" ]; then
echo "DATABASE_URL is required for dev DB guard" >&2
exit 42
fi
case "$database_url" in
*d601-tcp-egress-gateway*|*74.48.78.17:15432*|*database:5432/unidesk*|*/unidesk|*/unidesk\?*)
echo "refusing production-looking DATABASE_URL in unidesk-dev: $database_url" >&2
exit 42
;;
esac
case "$database_url" in
postgres://*@postgres-dev:5432/"$expected_name"|\
postgresql://*@postgres-dev:5432/"$expected_name"|\
postgres://*@postgres-dev.unidesk-dev:5432/"$expected_name"|\
postgresql://*@postgres-dev.unidesk-dev:5432/"$expected_name"|\
postgres://*@postgres-dev.unidesk-dev.svc:5432/"$expected_name"|\
postgresql://*@postgres-dev.unidesk-dev.svc:5432/"$expected_name"|\
postgres://*@postgres-dev.unidesk-dev.svc.cluster.local:5432/"$expected_name"|\
postgresql://*@postgres-dev.unidesk-dev.svc.cluster.local:5432/"$expected_name")
exit 0
;;
*)
echo "DATABASE_URL must target postgres-dev in unidesk-dev and database $expected_name: $database_url" >&2
exit 42
;;
esac
---
apiVersion: v1
kind: ConfigMap
metadata:
name: unidesk-dev-db-init
namespace: unidesk-dev
labels:
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
data:
001_unidesk_dev_schema.sql: |
BEGIN;
CREATE TABLE IF NOT EXISTS unidesk_environment_identity (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
INSERT INTO unidesk_environment_identity (key, value, updated_at)
VALUES
('environment', 'dev', now()),
('namespace', 'unidesk-dev', now()),
('database', 'unidesk_dev', now()),
('provider_id', 'D601-dev', now()),
('deploy_ref', 'origin/deploy/dev', now())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = now();
CREATE TABLE IF NOT EXISTS unidesk_nodes (
provider_id TEXT PRIMARY KEY,
name TEXT NOT NULL,
labels JSONB NOT NULL DEFAULT '{}'::jsonb,
status TEXT NOT NULL,
connected_at TIMESTAMPTZ,
last_heartbeat TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_events (
id BIGSERIAL PRIMARY KEY,
type TEXT NOT NULL,
source TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_tasks (
id TEXT PRIMARY KEY,
provider_id TEXT NOT NULL,
command TEXT NOT NULL,
status TEXT NOT NULL,
payload JSONB NOT NULL DEFAULT '{}'::jsonb,
result JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_scheduled_tasks (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
description TEXT NOT NULL DEFAULT '',
enabled BOOLEAN NOT NULL DEFAULT true,
schedule_json JSONB NOT NULL DEFAULT '{}'::jsonb,
action_json JSONB NOT NULL DEFAULT '{}'::jsonb,
concurrency_policy TEXT NOT NULL DEFAULT 'skip',
next_run_at TIMESTAMPTZ,
last_run_at TIMESTAMPTZ,
last_run_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_scheduled_task_runs (
id TEXT PRIMARY KEY,
schedule_id TEXT NOT NULL REFERENCES unidesk_scheduled_tasks(id) ON DELETE CASCADE,
trigger_type TEXT NOT NULL,
status TEXT NOT NULL,
task_id TEXT,
result JSONB,
error TEXT,
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
duration_ms BIGINT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_node_docker_status (
provider_id TEXT PRIMARY KEY,
status JSONB NOT NULL DEFAULT '{}'::jsonb,
collected_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_node_system_status (
provider_id TEXT PRIMARY KEY,
status JSONB NOT NULL DEFAULT '{}'::jsonb,
collected_at TIMESTAMPTZ,
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_node_metric_samples (
id BIGSERIAL PRIMARY KEY,
provider_id TEXT NOT NULL,
collected_at TIMESTAMPTZ NOT NULL,
cpu_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
memory_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
disk_percent DOUBLE PRECISION NOT NULL DEFAULT 0,
sample JSONB NOT NULL DEFAULT '{}'::jsonb,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS unidesk_code_queue_tasks (
id TEXT PRIMARY KEY,
queue_id TEXT NOT NULL DEFAULT 'default',
status TEXT NOT NULL,
provider_id TEXT NOT NULL DEFAULT 'D601-dev',
execution_mode TEXT NOT NULL DEFAULT 'default',
model TEXT NOT NULL,
cwd TEXT NOT NULL,
prompt TEXT NOT NULL,
base_prompt TEXT NOT NULL DEFAULT '',
reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb,
reference_injection JSONB,
reasoning_effort TEXT,
max_attempts INTEGER NOT NULL,
current_attempt INTEGER NOT NULL DEFAULT 0,
current_mode TEXT,
codex_thread_id TEXT,
active_turn_id TEXT,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
read_at TIMESTAMPTZ,
last_error TEXT,
last_judge JSONB,
output_count INTEGER NOT NULL DEFAULT 0,
event_count INTEGER NOT NULL DEFAULT 0,
attempt_count INTEGER NOT NULL DEFAULT 0,
last_output_seq BIGINT NOT NULL DEFAULT 0,
task_json JSONB NOT NULL
);
CREATE TABLE IF NOT EXISTS unidesk_code_queue_queues (
id TEXT PRIMARY KEY,
name TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE IF NOT EXISTS unidesk_code_queue_workdirs (
provider_id TEXT NOT NULL,
execution_mode TEXT NOT NULL DEFAULT 'default',
path TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
PRIMARY KEY (provider_id, execution_mode, path)
);
CREATE TABLE IF NOT EXISTS unidesk_code_queue_notifications (
id TEXT PRIMARY KEY,
kind TEXT NOT NULL,
dedup_key TEXT NOT NULL,
target TEXT NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ NOT NULL,
attempts INTEGER NOT NULL DEFAULT 0,
next_attempt_at TIMESTAMPTZ NOT NULL,
last_error TEXT,
sent_at TIMESTAMPTZ
);
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS queue_id TEXT NOT NULL DEFAULT 'default';
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS provider_id TEXT NOT NULL DEFAULT 'D601-dev';
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default';
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS base_prompt TEXT NOT NULL DEFAULT '';
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_task_ids JSONB NOT NULL DEFAULT '[]'::jsonb;
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS reference_injection JSONB;
ALTER TABLE unidesk_code_queue_tasks ADD COLUMN IF NOT EXISTS read_at TIMESTAMPTZ;
ALTER TABLE unidesk_code_queue_queues ADD COLUMN IF NOT EXISTS name TEXT NOT NULL DEFAULT '';
ALTER TABLE unidesk_code_queue_workdirs ADD COLUMN IF NOT EXISTS execution_mode TEXT NOT NULL DEFAULT 'default';
CREATE INDEX IF NOT EXISTS idx_unidesk_nodes_status ON unidesk_nodes(status);
CREATE INDEX IF NOT EXISTS idx_unidesk_events_created_at ON unidesk_events(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_provider_status ON unidesk_tasks(provider_id, status);
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_updated_at ON unidesk_tasks(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_tasks_status_updated_at ON unidesk_tasks(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_tasks_next_run ON unidesk_scheduled_tasks(enabled, next_run_at);
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_schedule_updated ON unidesk_scheduled_task_runs(schedule_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_scheduled_task_runs_status_updated ON unidesk_scheduled_task_runs(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_node_docker_status_updated_at ON unidesk_node_docker_status(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_node_system_status_updated_at ON unidesk_node_system_status(updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_node_metric_samples_provider_time ON unidesk_node_metric_samples(provider_id, collected_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_status_updated ON unidesk_code_queue_tasks(status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_queue_status_updated ON unidesk_code_queue_tasks(queue_id, status, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_provider_updated ON unidesk_code_queue_tasks(provider_id, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_execution_mode_updated ON unidesk_code_queue_tasks(execution_mode, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_created ON unidesk_code_queue_tasks(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_queue_created ON unidesk_code_queue_tasks(queue_id, created_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_unread_terminal ON unidesk_code_queue_tasks(queue_id, updated_at DESC) WHERE read_at IS NULL AND status IN ('succeeded', 'failed', 'canceled');
CREATE INDEX IF NOT EXISTS idx_unidesk_code_queue_tasks_model_updated ON unidesk_code_queue_tasks(model, updated_at DESC);
CREATE INDEX IF NOT EXISTS idx_code_queue_tasks_list ON unidesk_code_queue_tasks(status, updated_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS idx_code_queue_tasks_queue_updated ON unidesk_code_queue_tasks(queue_id, updated_at DESC, id DESC);
INSERT INTO unidesk_code_queue_queues (id, name, created_at, updated_at)
VALUES ('default', 'default', now(), now())
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, updated_at = GREATEST(unidesk_code_queue_queues.updated_at, EXCLUDED.updated_at);
COMMIT;
---
apiVersion: v1
kind: Service
metadata:
name: postgres-dev
namespace: unidesk-dev
labels:
app.kubernetes.io/name: postgres-dev
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: postgres-dev
unidesk.ai/environment: dev
ports:
- name: pg
port: 5432
targetPort: pg
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres-dev
namespace: unidesk-dev
labels:
app.kubernetes.io/name: postgres-dev
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
serviceName: postgres-dev
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: postgres-dev
unidesk.ai/environment: dev
template:
metadata:
labels:
app.kubernetes.io/name: postgres-dev
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
unidesk.ai/node-id: D601
spec:
nodeSelector:
unidesk.ai/node-id: D601
terminationGracePeriodSeconds: 30
securityContext:
fsGroup: 70
containers:
- name: postgres
image: postgres:16-alpine
imagePullPolicy: IfNotPresent
args:
- -c
- shared_buffers=64MB
- -c
- max_connections=30
- -c
- work_mem=4MB
- -c
- maintenance_work_mem=64MB
- -c
- effective_cache_size=256MB
ports:
- name: pg
containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: unidesk-dev-runtime-secrets
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: unidesk-dev-runtime-secrets
key: POSTGRES_PASSWORD
- name: POSTGRES_DB
valueFrom:
secretKeyRef:
name: unidesk-dev-runtime-secrets
key: POSTGRES_DB
volumeMounts:
- name: postgres-dev-data
mountPath: /var/lib/postgresql/data
readinessProbe:
exec:
command:
- sh
- -ec
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 18
livenessProbe:
exec:
command:
- sh
- -ec
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
startupProbe:
exec:
command:
- sh
- -ec
- pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB"
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 60
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeClaimTemplates:
- metadata:
name: postgres-dev-data
labels:
app.kubernetes.io/name: postgres-dev
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: batch/v1
kind: Job
metadata:
name: unidesk-dev-db-migrate
namespace: unidesk-dev
labels:
app.kubernetes.io/name: unidesk-dev-db-migrate
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
backoffLimit: 6
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: unidesk-dev-db-migrate
app.kubernetes.io/part-of: unidesk
unidesk.ai/environment: dev
spec:
restartPolicy: OnFailure
nodeSelector:
unidesk.ai/node-id: D601
containers:
- name: migrate
image: postgres:16-alpine
imagePullPolicy: IfNotPresent
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: unidesk-dev-runtime-secrets
key: DATABASE_URL
- name: UNIDESK_DEV_DATABASE_NAME
valueFrom:
configMapKeyRef:
name: unidesk-dev-runtime-config
key: UNIDESK_DEV_DATABASE_NAME
command:
- /bin/sh
- -ec
args:
- |
/etc/unidesk-dev-guard/guard-dev-db-url.sh "$DATABASE_URL"
until pg_isready -d "$DATABASE_URL"; do
echo "waiting for postgres-dev"
sleep 2
done
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f /migrations/001_unidesk_dev_schema.sql
volumeMounts:
- name: db-guard
mountPath: /etc/unidesk-dev-guard
readOnly: true
- name: db-init
mountPath: /migrations
readOnly: true
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 250m
memory: 256Mi
volumes:
- name: db-guard
configMap:
name: unidesk-dev-db-guard
defaultMode: 0555
- name: db-init
configMap:
name: unidesk-dev-db-init