chore(craft): drop dead build configs and trim comments (#11543)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
WenxiandClaude Opus 4.7 authored and GitHub committed 2026-05-29 19:57:15 +00:00
1 parent e1dd4df6c4
commit 06bcd26fb5
5 files changed
+37 -145

No files matched your search

+37 -130
View File
@@ -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"
@@ -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")
@@ -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
@@ -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
@@ -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