From 06bcd26fb5cc7c7707d2e2f47bb64412ee55bd02 Mon Sep 17 00:00:00 2001 From: Wenxi Date: Fri, 29 May 2026 12:57:15 -0700 Subject: [PATCH] chore(craft): drop dead build configs and trim comments (#11543) Co-authored-by: Claude Opus 4.7 (1M context) --- backend/onyx/server/features/build/configs.py | 167 ++++-------------- .../sandbox/test_sandbox_backend_selection.py | 9 - .../docker-compose.prod-no-letsencrypt.yml | 2 - .../docker_compose/docker-compose.prod.yml | 2 - deployment/docker_compose/docker-compose.yml | 2 - 5 files changed, 37 insertions(+), 145 deletions(-) diff --git a/backend/onyx/server/features/build/configs.py b/backend/onyx/server/features/build/configs.py index f6335554e0..ae7221899a 100644 --- a/backend/onyx/server/features/build/configs.py +++ b/backend/onyx/server/features/build/configs.py @@ -4,54 +4,11 @@ from pathlib import Path class SandboxBackend(str, Enum): - """Backend mode for sandbox operations. - - KUBERNETES: Production + dev (kind) - full snapshots and cleanup. - DOCKER: Self-hosted docker-compose - api_server drives the Docker Engine. - """ - KUBERNETES = "kubernetes" DOCKER = "docker" -def _parse_sandbox_backend(raw: str) -> SandboxBackend: - """Parse SANDBOX_BACKEND with a friendly error on the retired ``local`` value. - - The local backend was removed in favour of the kubernetes (kind) dev flow - documented in ``docs/dev/local-kubernetes.md``. We raise a deliberate - startup error rather than the bare ``ValueError`` from the enum - constructor so the operator gets pointed at the migration path. - """ - if raw.lower() == "local": - raise RuntimeError( - "SANDBOX_BACKEND=local is no longer supported. The local sandbox " - "backend has been removed; use the kind-based Kubernetes dev flow. " - "See docs/dev/local-kubernetes.md (run `make craft-up`) and unset " - "SANDBOX_BACKEND in your environment (it now defaults to " - "'kubernetes')." - ) - try: - return SandboxBackend(raw) - except ValueError as e: - raise RuntimeError( - f"SANDBOX_BACKEND={raw!r} is not a valid value. Allowed values: " - f"{[b.value for b in SandboxBackend]!r}. See " - "docs/dev/local-kubernetes.md for the recommended dev setup." - ) from e - - -# Sandbox backend mode (controls snapshot and cleanup behavior) -# "kubernetes" = full snapshots and cleanup (production Helm/cloud + dev kind) -# "docker" = full snapshots and cleanup (self-hosted docker-compose) -SANDBOX_BACKEND = _parse_sandbox_backend( - os.environ.get("SANDBOX_BACKEND", "kubernetes") -) - -# Base directory path for persistent document storage (local filesystem) -# Example: /var/onyx/file-system or /app/file-system -PERSISTENT_DOCUMENT_STORAGE_PATH = os.environ.get( - "PERSISTENT_DOCUMENT_STORAGE_PATH", "/app/file-system" -) +SANDBOX_BACKEND = SandboxBackend(os.environ.get("SANDBOX_BACKEND", "kubernetes")) _THIS_FILE = Path(__file__) @@ -59,19 +16,11 @@ SKILLS_TEMPLATE_PATH = str( _THIS_FILE.parent / "sandbox" / "kubernetes" / "docker" / "skills" ) -# Sandbox agent configuration -SANDBOX_AGENT_COMMAND = os.environ.get("SANDBOX_AGENT_COMMAND", "opencode").split() - -# OpenCode disabled tools (comma-separated list) -# Available tools: bash, edit, write, read, grep, glob, list, lsp, patch, -# skill, todowrite, todoread, webfetch, question -# Example: "question,webfetch" to disable user questions and web fetching _disabled_tools_str = os.environ.get("OPENCODE_DISABLED_TOOLS", "question") OPENCODE_DISABLED_TOOLS: list[str] = [ t.strip() for t in _disabled_tools_str.split(",") if t.strip() ] -# Sandbox lifecycle configuration SANDBOX_IDLE_TIMEOUT_SECONDS = int( os.environ.get("SANDBOX_IDLE_TIMEOUT_SECONDS", "3600") ) @@ -79,16 +28,9 @@ SANDBOX_MAX_CONCURRENT_PER_ORG = int( os.environ.get("SANDBOX_MAX_CONCURRENT_PER_ORG", "10") ) -# Sandbox snapshot storage -SANDBOX_SNAPSHOTS_BUCKET = os.environ.get( - "SANDBOX_SNAPSHOTS_BUCKET", "sandbox-snapshots" -) - -# Next.js preview server port range SANDBOX_NEXTJS_PORT_START = int(os.environ.get("SANDBOX_NEXTJS_PORT_START", "3010")) SANDBOX_NEXTJS_PORT_END = int(os.environ.get("SANDBOX_NEXTJS_PORT_END", "3100")) -# File upload configuration MAX_UPLOAD_FILE_SIZE_MB = int(os.environ.get("BUILD_MAX_UPLOAD_FILE_SIZE_MB", "50")) MAX_UPLOAD_FILE_SIZE_BYTES = MAX_UPLOAD_FILE_SIZE_MB * 1024 * 1024 MAX_UPLOAD_FILES_PER_SESSION = int( @@ -99,89 +41,72 @@ MAX_TOTAL_UPLOAD_SIZE_BYTES = MAX_TOTAL_UPLOAD_SIZE_MB * 1024 * 1024 ATTACHMENTS_DIRECTORY = "attachments" # ============================================================================ -# Kubernetes Sandbox Configuration -# Only used when SANDBOX_BACKEND = "kubernetes" +# Kubernetes sandbox (SANDBOX_BACKEND=kubernetes) # ============================================================================ -# Namespace where sandbox pods are created SANDBOX_NAMESPACE = os.environ.get("SANDBOX_NAMESPACE", "onyx-sandboxes") -# Container image for sandbox pods -# Should include Next.js template, opencode CLI, and agent skills SANDBOX_CONTAINER_IMAGE = os.environ.get( "SANDBOX_CONTAINER_IMAGE", "onyxdotapp/sandbox:v0.1.44" ) -# S3 bucket for sandbox file storage (snapshots, knowledge files, uploads) # Path structure: s3://{bucket}/{tenant_id}/snapshots/{session_id}/{snapshot_id}.tar.gz # s3://{bucket}/{tenant_id}/knowledge/{user_id}/ # s3://{bucket}/{tenant_id}/uploads/{session_id}/ SANDBOX_S3_BUCKET = os.environ.get("SANDBOX_S3_BUCKET", "onyx-sandbox-files") -# Service account for sandbox pods (needs IRSA for S3 snapshot access) +# Needs IRSA for S3 snapshot access. SANDBOX_SERVICE_ACCOUNT_NAME = os.environ.get( "SANDBOX_SERVICE_ACCOUNT_NAME", "sandbox-file-sync" ) ENABLE_CRAFT = os.environ.get("ENABLE_CRAFT", "false").lower() == "true" -# Provider types Onyx Craft can run on (v0), in priority order. A provider is -# only usable if the requesting user can access it (can_user_access_llm_provider). -# Keep in sync with BUILD_MODE_PROVIDERS keys in the frontend -# (web/src/app/craft/onboarding/constants.ts); enforced by +# Keep in sync with BUILD_MODE_PROVIDERS in +# web/src/app/craft/onboarding/constants.ts; enforced by # test_build_mode_provider_types_sync.py. BUILD_MODE_ALLOWED_PROVIDER_TYPES = ["anthropic", "openai", "openrouter"] - -# Recommended (default) Craft model per provider type. Keeps the headless/wake -# default model consistent with the frontend's interactive default. Keep in sync -# with the recommended models in BUILD_MODE_PROVIDERS (frontend); enforced by -# test_build_mode_provider_types_sync.py. BUILD_MODE_RECOMMENDED_MODEL_BY_TYPE = { "anthropic": "claude-opus-4-7", "openai": "gpt-5.5", "openrouter": "moonshotai/kimi-k2.6", } -# Dev/debug-only: when true, exposes a frontend button + SSE endpoint that -# tails the user's sandbox pod's opencode-serve container logs. Never -# enable in prod — the logs include LLM I/O and tool invocations that may -# contain sensitive data. Disabled by default; the SSE endpoint 404s when -# this is false so the surface is gone (not just hidden). +# Dev/debug-only: exposes an SSE endpoint that tails the sandbox pod's +# opencode-serve container logs. Never enable in prod — the logs include LLM +# I/O and tool invocations that may contain sensitive data. When false, the +# endpoint 404s so the surface is gone, not just hidden. ENABLE_OPENCODE_DEBUGGING = ( os.environ.get("ENABLE_OPENCODE_DEBUGGING", "false").lower() == "true" ) -# Internal URL the sandbox uses to reach the Onyx API server. # Must be set when SANDBOX_BACKEND=kubernetes (no default — varies per deployment). SANDBOX_API_SERVER_URL = os.environ.get("SANDBOX_API_SERVER_URL", "") -# Per-pod resource requests/limits. Defaults match production sizing for -# real bun/npm/python workloads. CI overrides these in kind clusters where +# Defaults match production sizing. CI overrides these in kind clusters where # the runner only has 4 vCPU and we provision 4+ sandbox pods concurrently; -# k8s scheduler honors requests, so the production defaults would prevent -# all-but-one pod from being scheduled at the same time. +# the k8s scheduler honors requests, so production defaults would block all +# but one pod from being scheduled at the same time. SANDBOX_POD_CPU_REQUEST = os.environ.get("SANDBOX_POD_CPU_REQUEST", "1000m") SANDBOX_POD_MEMORY_REQUEST = os.environ.get("SANDBOX_POD_MEMORY_REQUEST", "2Gi") SANDBOX_POD_CPU_LIMIT = os.environ.get("SANDBOX_POD_CPU_LIMIT", "2000m") SANDBOX_POD_MEMORY_LIMIT = os.environ.get("SANDBOX_POD_MEMORY_LIMIT", "10Gi") # ============================================================================ -# Sandbox Egress Proxy Configuration -# Consumed by both the api-server (building sandbox pod specs) and the -# proxy itself (on boot). +# Sandbox egress proxy # ============================================================================ -# Empty SANDBOX_PROXY_HOST disables proxy wiring for tests/dev (the -# initContainer is skipped and HTTPS_PROXY isn't set on the sandbox). +# Empty host disables proxy wiring for tests/dev (the initContainer is skipped +# and HTTPS_PROXY isn't set on the sandbox). SANDBOX_PROXY_HOST = os.environ.get("SANDBOX_PROXY_HOST", "") SANDBOX_PROXY_PORT = int(os.environ.get("SANDBOX_PROXY_PORT", "8080")) SANDBOX_PROXY_LISTEN_PORT = int(os.environ.get("SANDBOX_PROXY_LISTEN_PORT", "8080")) SANDBOX_PROXY_HEALTHZ_PORT = int(os.environ.get("SANDBOX_PROXY_HEALTHZ_PORT", "8081")) -# Namespace the proxy runs in. The CA Secret lives here; the CA -# ConfigMap is projected into SANDBOX_NAMESPACE so sandboxes can mount -# it (K8s does not allow cross-namespace ConfigMap mounts). +# The CA Secret lives here; the CA ConfigMap is projected into +# SANDBOX_NAMESPACE so sandboxes can mount it (K8s does not allow +# cross-namespace ConfigMap mounts). SANDBOX_PROXY_NAMESPACE = os.environ.get("SANDBOX_PROXY_NAMESPACE", "onyx") SANDBOX_PROXY_CA_SECRET = os.environ.get("SANDBOX_PROXY_CA_SECRET", "sandbox-proxy-ca") @@ -190,64 +115,51 @@ SANDBOX_PROXY_CA_CONFIGMAP = os.environ.get( ) # ============================================================================ -# Docker Sandbox Configuration -# Only used when SANDBOX_BACKEND = "docker" (self-hosted docker-compose) +# Docker sandbox (SANDBOX_BACKEND=docker, self-hosted docker-compose) # ============================================================================ -# Docker socket path on the api_server host. Mounted into the api_server -# container; api_server uses this to drive sandbox container lifecycle. +# Mounted into the api_server container; api_server uses this to drive +# sandbox container lifecycle. SANDBOX_DOCKER_SOCKET = os.environ.get("SANDBOX_DOCKER_SOCKET", "/var/run/docker.sock") -# Bridge network for sandbox containers. Sandbox containers join only this -# network and never compose's default network, isolating them from -# api_server, postgres, redis, etc. +# Sandbox containers join only this network and never compose's default +# network, isolating them from api_server, postgres, redis, etc. SANDBOX_DOCKER_NETWORK = os.environ.get("SANDBOX_DOCKER_NETWORK", "onyx_craft_sandbox") -# Prefix for the per-sandbox named volumes that hold ``/workspace/sessions``. SANDBOX_DOCKER_VOLUME_PREFIX = os.environ.get( "SANDBOX_DOCKER_VOLUME_PREFIX", "onyx-craft-sandbox-" ) -# Container resource limits. Memory accepts docker-style suffixes (``2g``). -# Defaults match the Kubernetes sandbox pod's *requests* (1 CPU / 2Gi), -# not its limits (2 CPU / 10Gi). Single-VM docker-compose deployments rarely -# have the headroom to over-commit each sandbox to 10Gi. +# Defaults match the Kubernetes sandbox pod's *requests* (1 CPU / 2Gi), not +# its limits (2 CPU / 10Gi). Single-VM docker-compose deployments rarely have +# the headroom to over-commit each sandbox to 10Gi. SANDBOX_DOCKER_MEMORY_LIMIT = os.environ.get("SANDBOX_DOCKER_MEMORY_LIMIT", "2g") SANDBOX_DOCKER_CPU_LIMIT = float(os.environ.get("SANDBOX_DOCKER_CPU_LIMIT", "1.0")) # ============================================================================ -# SSE Streaming Configuration +# SSE / opencode-serve # ============================================================================ -# SSE keepalive interval in seconds - send keepalive comment if no events SSE_KEEPALIVE_INTERVAL = float(os.environ.get("SSE_KEEPALIVE_INTERVAL", "15.0")) -# ============================================================================ -# Opencode-serve Configuration -# ============================================================================ - # Wall-clock budget for one user-message turn against opencode-serve. SANDBOX_TURN_TIMEOUT_SECONDS = float( os.environ.get("SANDBOX_TURN_TIMEOUT_SECONDS", "900.0") ) -# Port `opencode serve` listens on inside the sandbox container. # Match against the EXPOSE directive in the sandbox Dockerfile. OPENCODE_SERVE_PORT = int(os.environ.get("OPENCODE_SERVE_PORT", "4096")) # Env var inside the sandbox container that holds the per-pod HTTP Basic -# password for opencode serve. Internal contract — the api_server writes -# this name and opencode-serve reads it, so both ends must agree. Not -# operator-tunable. +# password for opencode serve. Internal contract — api_server writes this +# name and opencode-serve reads it, so both ends must agree. OPENCODE_SERVER_PASSWORD = "OPENCODE_SERVER_PASSWORD" -# Username for HTTP Basic Auth against opencode serve. Opencode's serve -# implementation hard-codes the username to "opencode" when only -# OPENCODE_SERVER_PASSWORD is set; using any other value yields a 401 -# (verified empirically against opencode 1.15.7, see test report). +# Opencode's serve implementation hard-codes the username to "opencode" when +# only OPENCODE_SERVER_PASSWORD is set; any other value yields a 401 +# (verified against opencode 1.15.7). OPENCODE_SERVER_USERNAME = "opencode" -# Per-request HTTP timeouts when talking to opencode serve, in seconds. OPENCODE_SERVE_CONNECT_TIMEOUT = float( os.environ.get("OPENCODE_SERVE_CONNECT_TIMEOUT", "5.0") ) @@ -261,37 +173,32 @@ OPENCODE_SERVE_EVENT_READ_TIMEOUT = float( ) # ============================================================================ -# Rate Limiting Configuration +# Rate limiting # ============================================================================ -# Base rate limit for paid/subscribed users (messages per week) -# Free users always get 5 messages total (not configurable) -# Per-user overrides are managed via PostHog feature flag "craft-has-usage-limits" +# Messages per week. Free users always get 5 messages total (not configurable). +# Per-user overrides are managed via the PostHog feature flag +# "craft-has-usage-limits". CRAFT_PAID_USER_RATE_LIMIT = int(os.environ.get("CRAFT_PAID_USER_RATE_LIMIT", "25")) # ============================================================================ -# User Library Configuration -# For user-uploaded raw files (xlsx, pptx, docx, etc.) in Craft +# User Library (user-uploaded raw files: xlsx, pptx, docx, etc.) # ============================================================================ -# Maximum size per file in MB (default 500MB) USER_LIBRARY_MAX_FILE_SIZE_MB = int( os.environ.get("USER_LIBRARY_MAX_FILE_SIZE_MB", "500") ) USER_LIBRARY_MAX_FILE_SIZE_BYTES = USER_LIBRARY_MAX_FILE_SIZE_MB * 1024 * 1024 -# Maximum total storage per user in GB (default 10GB) USER_LIBRARY_MAX_TOTAL_SIZE_GB = int( os.environ.get("USER_LIBRARY_MAX_TOTAL_SIZE_GB", "10") ) USER_LIBRARY_MAX_TOTAL_SIZE_BYTES = USER_LIBRARY_MAX_TOTAL_SIZE_GB * 1024 * 1024 * 1024 -# Maximum files per single upload request (default 100) USER_LIBRARY_MAX_FILES_PER_UPLOAD = int( os.environ.get("USER_LIBRARY_MAX_FILES_PER_UPLOAD", "100") ) -# String constants for User Library entities USER_LIBRARY_CONNECTOR_NAME = "User Library" USER_LIBRARY_CREDENTIAL_NAME = "User Library Credential" USER_LIBRARY_SOURCE_DIR = "user_library" diff --git a/backend/tests/unit/onyx/server/features/build/sandbox/test_sandbox_backend_selection.py b/backend/tests/unit/onyx/server/features/build/sandbox/test_sandbox_backend_selection.py index c08e6113fc..79917dda71 100644 --- a/backend/tests/unit/onyx/server/features/build/sandbox/test_sandbox_backend_selection.py +++ b/backend/tests/unit/onyx/server/features/build/sandbox/test_sandbox_backend_selection.py @@ -59,12 +59,3 @@ def test_sandbox_backend_enum_includes_docker_and_kubernetes() -> None: assert SandboxBackend.KUBERNETES.value == "kubernetes" assert configs.SandboxBackend("docker") is SandboxBackend.DOCKER assert configs.SandboxBackend("kubernetes") is SandboxBackend.KUBERNETES - - -def test_local_backend_string_raises_helpful_error() -> None: - """``SANDBOX_BACKEND=local`` is no longer supported; the parser raises a - pointed startup error with a doc pointer rather than the bare - ``ValueError`` from the enum constructor. - """ - with pytest.raises(RuntimeError, match="local-kubernetes.md"): - configs._parse_sandbox_backend("local") diff --git a/deployment/docker_compose/docker-compose.prod-no-letsencrypt.yml b/deployment/docker_compose/docker-compose.prod-no-letsencrypt.yml index e0625fb7f6..1a1ad20f0b 100644 --- a/deployment/docker_compose/docker-compose.prod-no-letsencrypt.yml +++ b/deployment/docker_compose/docker-compose.prod-no-letsencrypt.yml @@ -33,7 +33,6 @@ services: - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:-minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:-minioadmin} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} env_file: - path: .env required: false @@ -82,7 +81,6 @@ services: - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:-minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:-minioadmin} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} env_file: - path: .env required: false diff --git a/deployment/docker_compose/docker-compose.prod.yml b/deployment/docker_compose/docker-compose.prod.yml index 5121ea2691..d93e06da9b 100644 --- a/deployment/docker_compose/docker-compose.prod.yml +++ b/deployment/docker_compose/docker-compose.prod.yml @@ -34,7 +34,6 @@ services: - S3_ENDPOINT_URL=${S3_ENDPOINT_URL:-http://minio:9000} - S3_AWS_ACCESS_KEY_ID=${S3_AWS_ACCESS_KEY_ID:-minioadmin} - S3_AWS_SECRET_ACCESS_KEY=${S3_AWS_SECRET_ACCESS_KEY:-minioadmin} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} env_file: - path: .env required: false @@ -92,7 +91,6 @@ services: # API Server connection for Discord bot message processing - API_SERVER_PROTOCOL=${API_SERVER_PROTOCOL:-http} - API_SERVER_HOST=${API_SERVER_HOST:-api_server} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} env_file: - path: .env required: false diff --git a/deployment/docker_compose/docker-compose.yml b/deployment/docker_compose/docker-compose.yml index 35dfdb5d0d..8377d9ce9d 100644 --- a/deployment/docker_compose/docker-compose.yml +++ b/deployment/docker_compose/docker-compose.yml @@ -90,7 +90,6 @@ services: - OUTPUTS_TEMPLATE_PATH=${OUTPUTS_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/outputs} - VENV_TEMPLATE_PATH=${VENV_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/venv} - WEB_TEMPLATE_PATH=${WEB_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/outputs/web} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} # PRODUCTION: Uncomment the line below to use if IAM_AUTH is true and you are using iam auth for postgres # volumes: # - ./bundle.pem:/app/bundle.pem:ro @@ -176,7 +175,6 @@ services: - OUTPUTS_TEMPLATE_PATH=${OUTPUTS_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/outputs} - VENV_TEMPLATE_PATH=${VENV_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/venv} - WEB_TEMPLATE_PATH=${WEB_TEMPLATE_PATH:-/app/onyx/server/features/build/sandbox/kubernetes/docker/templates/outputs/web} - - PERSISTENT_DOCUMENT_STORAGE_PATH=${PERSISTENT_DOCUMENT_STORAGE_PATH:-/app/file-system} # PRODUCTION: Uncomment the line below to use if IAM_AUTH is true and you are using iam auth for postgres # volumes: # - ./bundle.pem:/app/bundle.pem:ro