Full environment variable reference for SAM. Use when adding, modifying, or documenting environment variables, configuring deployment, or working with Worker secrets.
Installs into .claude/skills of the current project.
Are you the author of Env Reference?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/raphaeltm-env-reference-simple-agent-manager)
---
name: env-reference
description: Full environment variable reference for SAM. Use when adding, modifying, or documenting environment variables, configuring deployment, or working with Worker secrets.
user-invocable: false
---
# SAM Environment Variable Reference
## GitHub Environment Secrets (GitHub Settings -> Environments -> production)
Uses `GH_*` prefix because GitHub Actions secret names cannot start with `GITHUB_*`.
| Type | Name | Required |
| -------- | -------------------------------------------------- | ------------------------------------------------------------------ |
| Variable | `BASE_DOMAIN` | Yes |
| Variable | `RESOURCE_PREFIX` | No (default: `sam`) |
| Variable | `PULUMI_STATE_BUCKET` | No (default: `sam-pulumi-state`) |
| Variable | `CF_CONTAINER_ENABLED` | No (default: `true`; set `false` to force VM runtime) |
| Variable | `D1_READ_REPLICATION_MODE` | No (default: `auto`; `disabled` removes read replicas) |
| Variable | `D1_SESSION_MODE` | No (default: `first-primary`; `disabled` pins reads to primary) |
| Variable | `D1_MIGRATION_CHURNING_TABLES` | No (may narrow the reviewed built-in retention/expiry table list) |
| Variable | `D1_MIGRATION_CHURNING_TABLE_MAX_DECREASE_PERCENT` | No (default: `50`; range: 0–100) |
| Secret | `CF_API_TOKEN` | Yes (requires Account → SSL and Certificates → Edit for Origin CA) |
| Secret | `CF_ACCOUNT_ID` | Yes |
| Secret | `CF_ZONE_ID` | Yes |
| Secret | `DEVCONTAINER_CACHE_CLOUDFLARE_API_TOKEN` | No (falls back to `CF_API_TOKEN`) |
| Secret | `DEVCONTAINER_CACHE_CLOUDFLARE_ACCOUNT_ID` | No (falls back to `CF_ACCOUNT_ID`) |
| Secret | `R2_ACCESS_KEY_ID` | Yes |
| Secret | `R2_SECRET_ACCESS_KEY` | Yes |
| Secret | `PULUMI_CONFIG_PASSPHRASE` | Yes |
| Secret | `GH_CLIENT_ID` | Yes |
| Secret | `GH_CLIENT_SECRET` | Yes |
| Secret | `GH_APP_ID` | Yes |
| Secret | `GH_APP_PRIVATE_KEY` | Yes |
| Secret | `GH_APP_SLUG` | Yes |
| Secret | `GH_WEBHOOK_SECRET` | Yes when GitHub App webhooks are active |
| Secret | `ENCRYPTION_KEY` | No (auto-generated) |
| Secret | `JWT_PRIVATE_KEY` | No (auto-generated) |
| Secret | `JWT_PUBLIC_KEY` | No (auto-generated) |
| Secret | `DEPLOY_SIGNING_PRIVATE_KEY` | No (auto-generated; override only) |
| Secret | `DEPLOY_SIGNING_PUBLIC_KEY` | No (derived during deploy; override only) |
| Secret | `VAPID_PRIVATE_KEY` | No (auto-generated; override only) |
| Secret | `VAPID_PUBLIC_KEY` | No (derived during deploy; override only) |
| Secret | `VAPID_SUBJECT` | No (generated contact URI; override only) |
| Secret | `TRIAL_CLAIM_TOKEN_SECRET` | No (auto-generated) |
| Variable | `ORIGIN_CA_CERT_VALIDITY_DAYS` | No (default: 7) |
`D1_MIGRATION_CHURNING_TABLES` is a comma-separated `<binding>.<table>` subset of the reviewed defaults: `DATABASE.deployment_releases`, `DATABASE.github_webhook_deliveries`, `DATABASE.project_files`, `DATABASE.registry_credential_rate_limits`, `DATABASE.session_snapshots`, `DATABASE.sessions`, `DATABASE.trial_waitlist`, `DATABASE.trigger_executions`, `DATABASE.verifications`, `DATABASE.webhook_deliveries`, and `OBSERVABILITY_DATABASE.platform_errors`. It may narrow but cannot expand this list; unset uses all reviewed defaults. `D1_MIGRATION_CHURNING_TABLE_MAX_DECREASE_PERCENT` accepts `0`–`100`; a decrease exactly at the configured limit is accepted and a larger decrease blocks.
`ORIGIN_CA_CERT` and `ORIGIN_CA_KEY` are legacy rotation inputs for nodes provisioned before per-node Origin CA CSR signing. They are not required for new node provisioning.
## GH* to GITHUB* Mapping (done by `configure-secrets.sh`)
```
GitHub Secret -> Cloudflare Worker Secret
GH_CLIENT_ID -> GITHUB_CLIENT_ID
GH_CLIENT_SECRET -> GITHUB_CLIENT_SECRET
GH_APP_ID -> GITHUB_APP_ID
GH_APP_PRIVATE_KEY -> GITHUB_APP_PRIVATE_KEY
GH_APP_SLUG -> GITHUB_APP_SLUG
GH_WEBHOOK_SECRET -> GITHUB_WEBHOOK_SECRET
```
Use `GH_WEBHOOK_SECRET` in GitHub Actions because secret names cannot start with `GITHUB_`. The Worker/runtime secret remains `GITHUB_WEBHOOK_SECRET`, and it must match the GitHub App webhook secret exactly.
## API Worker Runtime Environment Variables
See `apps/api/.env.example` for the full list. Key variables:
### Core
- `WRANGLER_PORT` — Local dev port (default: 8787)
- `BASE_DOMAIN` — Set automatically by sync scripts
- `SAM_INSTALLATION_ID` — Pulumi-generated, non-secret exact installation identity injected by generated deployment config. Missing or malformed values disable destructive provider-side orphan reconciliation; operators do not set this manually.
- `GITHUB_INSTALLATION_TOKEN_CACHE_TTL_SECONDS` — KV cache TTL for GitHub App installation tokens minted for workspace git access (default: `3000`)
- `GITHUB_INSTALLATION_TOKEN_REFRESH_MARGIN_SECONDS` — Cached GitHub App installation tokens at or inside this margin before `expiresAt` are ignored and refreshed (default: `300`; capped at `1800`, half the one-hour token lifetime)
- `CF_CONTAINER_ENABLED` — Enables Cloudflare Container instant-session runtime in generated deployment envs (default: `true`; set `false` to force VM runtime)
- `CF_CONTAINER_SLEEP_AFTER` — Container idle sleep duration for instant-session runtime (default: `1h`)
- `CF_CONTAINER_ACTIVE_WORK_MAX_MS` — Defensive maximum active-work keepalive duration (default: `7200000`)
- `CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS` — Active-work keepalive renewal interval (default: `300000`)
- `CF_CONTAINER_VM_AGENT_PORT` — vm-agent standalone HTTP port inside the raw container (default: `8080`)
- `CF_CONTAINER_PORT_READY_TIMEOUT_MS` — Max wait for vm-agent port readiness (default: `30000`)
- `CF_CONTAINER_WAKE_TIMEOUT_MS` — Max wait for launch, snapshot restore, and request readiness (default: `120000`)
- `CF_CONTAINER_RECOVERY_MAX_ATTEMPTS` — Max restore attempts before terminal status reconciliation (default and deployment-safety minimum: `2`)
- `INSTANT_STALE_CALLBACK_MARGIN_MS` — Freshness margin for rejecting destructive callbacks from superseded Instant containers (default: `60000`)
- `CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS` — Synchronous workspace creation and clone budget (default: `120000`)
- `CF_CONTAINER_CLONE_FILTER` — Git partial-clone filter (default: `blob:none`; `off` disables partial clone)
- `SESSION_SNAPSHOT_TTL_DAYS` — Retention from actual sleep; the scheduled Worker terminalizes the chat and deletes R2 state (default: `7`)
- `SESSION_SNAPSHOT_TOTAL_BUDGET_BYTES` — Maximum combined bytes accepted for snapshot artifacts (default: `268435456`)
- `SESSION_SNAPSHOT_ENTRY_THRESHOLD_BYTES` — Per-file threshold before snapshot content is visibly skipped (default: `268435456`)
- `SESSION_SNAPSHOT_TRANSFER_IDLE_TIMEOUT_MS` — Progress-idle timeout for snapshot upload/download (default: `30000`)
- `SESSION_SNAPSHOT_UPLOAD_URL_TTL_SECONDS` — Lifetime of direct R2 snapshot upload URLs (default: `900`)
- `SESSION_SNAPSHOT_REQUEST_TIMEOUT_MS` — Budget for vm-agent acceptance of the final checkpoint request (default: `300000`)
- `SESSION_SNAPSHOT_PROGRESS_IDLE_TIMEOUT_MS` — No-progress watchdog after a final checkpoint is accepted (default: `120000`)
- `SESSION_SNAPSHOT_POLL_INTERVAL_MS` — D1 poll interval while waiting for final checkpoint progress/completion (default: `1000`)
- `SESSION_SNAPSHOT_OPERATION_TIMEOUT` — VM-agent checkpoint/restore deadline, passed to new VM nodes and Instant containers as a Go duration (default: `15m`). Snapshot TaskRunner restore retries pin this duration plus `SESSION_SNAPSHOT_REQUEST_TIMEOUT_MS` at the first restore RPC; retries/restarts cannot renew it. Other steps retain the retry-count limit.
- `SESSION_SNAPSHOT_PROGRESS_REPORT_INTERVAL` — VM-agent snapshot progress callback throttle, passed to new VM nodes and Instant containers as a Go duration (default: `15s`)
- `SESSION_SNAPSHOT_PROGRESS_REPORT_TIMEOUT` — VM-agent snapshot progress callback timeout, passed to new VM nodes and Instant containers as a Go duration (default: `5s`)
- `SESSION_SNAPSHOT_JSON_BODY_MAX_BYTES` — Maximum snapshot coordination JSON body (default: `262144`)
- `SESSION_SNAPSHOT_R2_PREFIX` — Private R2 object prefix for session snapshots (default: `session-snapshots`)
- `SESSION_SNAPSHOT_RECOVERY_MAX_ATTEMPTS` — Maximum replacement-VM wake attempts before the sleeping session becomes unavailable (default: `3`)
- `SESSION_SNAPSHOT_RECOVERY_ATTEMPT_DECAY_MS` — How long a spent wake-attempt burst stays spent (default: `900000`)
- `SESSION_RECOVERY_LINEAGE_MAX_DEPTH` — Wake→wake links followed back to the conversation's first run when deciding whether a wake must stay in its original location (default: `256`)
- `SESSION_SLEEP_AFTER_MS` — Runtime-neutral idle time before automatic VM-session sleep; completed and failed tasks queue sleep immediately and drain for this long past the agent's last turn report (default: `900000`)
- `FAILED_TASK_PRESERVATION_MAX_WAIT_MS` — Longest a failed task's runtime waits for its work-preservation sleep, from the latest of the failure, an in-place wake and the agent's current turn start, before the sweep tears it down with a chat notice (default: `28800000`)
- `SESSION_SLEEP_SWEEP_BATCH_SIZE` — Maximum due VM sleeps atomically claimed by one scheduled sweep (default: `10`)
- `SESSION_SLEEP_RETRY_DELAY_MS` — Delay after a fail-closed automatic sleep attempt (default: `300000`)
- `SESSION_SLEEP_MAX_ATTEMPTS` — Failed-attempt ceiling of a bounded sleep-failure episode: at it the episode ends blocked (no teardown, chat notice), except a failed task's preservation, which then tears the runtime down and says so; also re-arms legacy rows exhausted below a raised value (default: `9`, clamped above `SESSION_SLEEP_FAILURE_MAX_ATTEMPTS`)
- `SESSION_SLEEP_FAILURE_MAX_ATTEMPTS` — Failed full-snapshot sleep attempts in one episode before the transcript-and-Git fallback is tried (default: `3`)
- `SESSION_SLEEP_FAILURE_MAX_ELAPSED_MS` — Time since the sleep episode began (with at least one failure) before the transcript-and-Git fallback is tried (default: `900000`, 15 min)
- `SESSION_SLEEP_CLAIM_LEASE_MS` — Reclaim timeout for an interrupted automatic-sleep claim (default: `600000`)
- `HARNESS_BACKGROUND_WORK_LEASE_MS` — Finite sleep-protection lease renewed by normalized harness background-work lifecycle signals (default: `300000`)
- `HARNESS_BACKGROUND_WORK_MAX_DURATION_MS` — Absolute ceiling, measured from the last harness lifecycle progress edge, on how long background work may defer sleep (default: `1800000`)
- `ACP_ACTIVITY_ADMISSION_ENABLED` — Enables Worker-side admission control/coalescing for redundant intermediate ACP activity callbacks (default: `true`)
- `ACP_ACTIVITY_COALESCE_WINDOW_MS` — Minimum interval between redundant intermediate ProjectData activity writes, and the first retry delay for a coalesced report whose flush hit a retryable ProjectData failure (including a CPU-limit reset or lost connection); each further retry doubles, capped at `ACP_ACTIVITY_COALESCE_TTL_MS`. Transitions and terminal/error reports bypass it (default: `2000`)
- `ACP_ACTIVITY_COALESCE_TTL_MS` — Maximum lifetime for a coalesced activity report before eviction leaves convergence to probe-backed reconciliation (default: `60000`)
- `ACP_ACTIVITY_COALESCE_MAX_PENDING` — Maximum pending coalesced activity reports retained by one Worker isolate (default: `512`)
- `ACP_ACTIVITY_BINDING_CACHE_TTL_MS` — Short-lived authorized ACP session binding cache used to avoid ProjectData reads during callback storms (default: `30000`)
- `ACP_ACTIVITY_BINDING_CACHE_MAX_ENTRIES` — Maximum cached ACP activity bindings retained by one Worker isolate (default: `2048`)
- `ACP_INTERACTIONS_ENABLED` — Durable ACP interaction creation kill switch. Runtime and UI consumers are wired, while creation remains disabled by default pending rollout (default: `false`)
- `ACP_INTERACTION_FORMS_ENABLED` — Independent form capability for conversation sessions only; requires the global interaction flag and a compatible runtime (default: `false`). URL elicitation remains unavailable.
- `ACP_INTERACTION_PERMISSION_TASK_DEADLINE_MS` / `ACP_INTERACTION_PERMISSION_CONVERSATION_DEADLINE_MS` / `ACP_INTERACTION_DEADLINE_MARGIN_MS` — Runtime permission deadlines for task and conversation sessions and the margin before an enclosing prompt deadline (defaults: `1800000` / `7200000` / `60000`)
- `ACP_INTERACTION_MAX_DEADLINE_MS` — Absolute deadline ceiling for runtime-created requests (default: `14400000`)
- `ACP_INTERACTION_MAX_PENDING_PER_SESSION` — Maximum pending durable ACP interactions per chat (default: `8`)
- `ACP_INTERACTION_REQUEST_MAX_BYTES`, `ACP_INTERACTION_OPTIONS_MAX_COUNT`, `ACP_INTERACTION_OPTION_ID_MAX_CHARS`, `ACP_INTERACTION_OPTION_NAME_MAX_CHARS`, `ACP_INTERACTION_FORM_SCHEMA_MAX_BYTES`, `ACP_INTERACTION_FORM_SCHEMA_MAX_PROPERTIES`, `ACP_INTERACTION_FORM_SCHEMA_MAX_ENUM` — Request/detail and schema bounds for encrypted ACP interaction payloads; option ID/name limits also bound form field IDs/choice values and labels (defaults: `32768`, `16`, `128`, `200`, `16384`, `20`, `50`)
- `ACP_INTERACTION_RUNTIME_RECEIPT_LIMIT` / `ACP_INTERACTION_RUNTIME_RESPONSE_MAX_BYTES` — Per-SessionHost idempotency receipt bound and maximum create/settle response body read by the runtime (defaults: `256` / `65536`)
- `ACP_INTERACTION_ANSWER_MAX_BYTES` / `ACP_INTERACTION_ANSWER_STRING_MAX_BYTES` — Answer decision and individual answer string bounds before encrypted storage (defaults: `16384` / `4096`)
- `ACP_INTERACTION_RETRY_DELAYS_MS` / `ACP_INTERACTION_RETRY_STEADY_MS` / `ACP_INTERACTION_DELIVERY_WINDOW_MS` — Durable answer outbox retry sequence, steady retry delay, and max post-answer retry window (defaults: `1000,5000,30000,120000,300000`, `300000`, `900000`)
- `ACP_INTERACTION_SENSITIVE_PURGE_MS`, `ACP_INTERACTION_SUMMARY_RETENTION_MS`, `ACP_INTERACTION_SUMMARY_LAST_SETTLED`, `ACP_INTERACTION_SNAPSHOT_LAST_SETTLED` — Sensitive encrypted payload purge, settled summary retention, and bounded snapshot controls (defaults: `3600000`, `2592000000`, `100`, `20`)
- `ACP_INTERACTION_EXPIRY_BATCH_SIZE`, `ACP_INTERACTION_OUTBOX_BATCH_SIZE`, `ACP_INTERACTION_DELIVERY_BATCH_SIZE` — Per-alarm limits for deadline settlement, due outbox selection, and no-wake VM delivery attempts (defaults: `25`, `25`, `1`)
- `ACP_INTERACTION_ALARM_WALL_TIME_MS` / `ACP_INTERACTION_ALARM_REARM_DELAY_MS` — Total alarm work budget and minimum re-arm delay (defaults: `15000` / `1000`)
Activity coalescing and binding caches are per Worker isolate. Delayed flushes carry their original observed event time, and ProjectData rejects stale writes so a delayed intermediate report cannot overwrite a newer idle/error state from another isolate.
- `SESSION_SNAPSHOT_RECOVERY_CLAIM_LEASE_MS` — Reclaim timeout for an interrupted replacement-runtime wake claim (default: `600000`)
- `SESSION_LIFECYCLE_ERROR_MAX_LENGTH` — Maximum stored session lifecycle and agent activity failure diagnostic length (default: `2048`)
- `SESSION_SNAPSHOT_PURGE_ENABLED` — Kill switch for expired snapshot cleanup in D1 and R2 (default: enabled)
- `SESSION_SNAPSHOT_PURGE_BATCH_SIZE` — Maximum expired snapshot rows deleted per run (default: `250`)
### Deployment Storage Retention
- `DEPLOYMENT_RELEASE_RETENTION_ENABLED` — Kill switch for superseded terminal release pruning (default: enabled)
- `DEPLOYMENT_RELEASE_RETENTION_COUNT` — Newest releases protected per deployment environment, in addition to observed-applied and non-terminal releases (default: `3`)
- `DEPLOYMENT_RELEASE_RETENTION_BATCH_SIZE` — Maximum terminal release rows deleted per run (default: `250`)
- `DEPLOYMENT_RELEASE_RETENTION_INTERVAL_HOURS` — Minimum interval between release retention runs (default: `24`)
- `DEPLOYMENT_RELEASE_RETENTION_LAST_RUN_KV_KEY` — KV interval marker (default: `cleanup:deployment-releases:last-run`)
- `DEPLOYMENT_RELEASE_RECONCILIATION_ENABLED` — Kill switch for stale nonterminal compose release reconciliation (default: enabled)
- `DEPLOYMENT_RELEASE_RECONCILIATION_BATCH_SIZE` — Maximum stale nonterminal releases terminalized before retention pruning in one run (default: `50`)
- `DEPLOYMENT_RELEASE_RECONCILIATION_STALE_HOURS` — Minimum release status age before reconciliation can mark a nonterminal release failed (default: `168`)
- `DEPLOYMENT_RELEASE_RECONCILIATION_ACTIVITY_GRACE_HOURS` — Recent release-event protection window for active fetch/apply work (default: `6`)
- `COMPOSE_IMAGE_ARTIFACT_CLEANUP_BATCH_SIZE` — Maximum abandoned compose archives deleted per daily run (default: `250`)
### Deployment Placement Defaults
- `DEPLOYMENT_DEFAULT_CPU_LIMIT_MILLIS` — Per-service CPU reservation when a normalized deployment manifest omits the whole resource block (default: `250`)
- `DEPLOYMENT_DEFAULT_MEMORY_LIMIT_MB` — Per-service memory limit and placement reservation when a deployment omits the whole resource block (default: `256`)
- `DEPLOYMENT_DEFAULT_ROOT_DISK_MB` — Per-service root-filesystem reservation for image layers, logs, and temporary files (default: `1024`)
### Guided Agent Credential Setup
- `MAX_CONCURRENT_SETUP_SESSIONS` — Concurrent Cloudflare Sandbox setup-session cap (default: `2`)
- `SETUP_SESSION_TTL_MS` — Setup-session lifetime before teardown (default: `900000`)
- `SETUP_SESSION_CAPTURE_POLL_MS` — Device-login and credential-capture poll interval (default: `3000`)
- `CODEX_DEVICE_AUTH_REQUEST_TIMEOUT_MS` — Codex app-server JSON-RPC request timeout (default: `30000`)
- `CLAUDE_SETUP_ENTER_DELAY_MS` — Delay before sending Enter as a separate stdin write after Claude's browser-displayed code is pasted into the CLI (default: `1000`)
- `CLAUDE_SETUP_EXCHANGE_TIMEOUT_MS` — Maximum wait for Claude's CLI exchange to finish after code submission (default: `120000`)
- `CLAUDE_SETUP_REJECTION_SETTLE_MS` — Wait for Ink redraws to settle before classifying the Claude CLI OAuth error line (default: `400`)
- `CLAUDE_SETUP_VERIFICATION_POLL_MS` — Poll interval for the browser-code handoff file inside the Claude setup sandbox (default: `500`)
- `CLAUDE_SETUP_TTY_COLUMNS` — PTY width used for `claude setup-token` to reduce opaque-token wrapping (default: `512`)
- `CLAUDE_SETUP_OUTPUT_BUFFER_BYTES` — Maximum in-memory Claude PTY output retained for parsing (default: `32768`)
- `CLAUDE_VERIFICATION_CODE_MAX_LENGTH` — Maximum accepted browser-displayed `code#state` length (default: `1024`)
- `CLAUDE_SETUP_ERROR_DETAIL_MAX_LENGTH` — Maximum sanitized Claude CLI diagnostic surfaced to the user (default: `160`)
- `CLAUDE_OAUTH_TOKEN_MAX_LENGTH` — Maximum captured Claude OAuth token length (default: `8192`)
- `SETUP_SESSION_SWEEP_MAX_CANDIDATES` — Maximum expired setup sessions torn down per sweep (default: `50`)
- `POOL_LEASE_BUFFER_MS` — Grace after the session TTL before a leaked setup-pool lease self-prunes (default: `300000`)
### Operational Control Loops
- `CRON_SWEEPS_ENABLED_KV_KEY` — Fail-open KV brake key for the five-minute operational sweep (default: `control-loops:cron-enabled`)
- `DO_ALARMS_ENABLED_KV_KEY` — Fail-open KV brake key shared by alarm-bearing DOs (default: `control-loops:alarms-enabled`)
- `CONTROL_LOOP_KILL_SWITCH_CACHE_MS` — In-memory brake cache, clamped to 30000ms (default: `30000`)
- `CONTROL_LOOP_DISABLED_ALARM_RETRY_MS` — Alarm recheck interval while disabled, clamped to at least 60000ms (default: `300000`)
- `CRON_FAILURE_NOTIFICATION_THROTTLE_MS` — Per-sweep failure-notification throttle backed by KV and an atomic per-user Notification DO claim (default: `3600000`)
- `CRON_FAILURE_NOTIFICATION_KV_PREFIX` — KV prefix for failure-notification throttle markers (default: `cron-failure-notification`)
- `NODE_LIFECYCLE_MAX_DESTROYING_AGE_MS` — Maximum destroying-state residence before DO self-cleanup (default: `86400000`)
- `NODE_WORKSPACE_IDLE_TIMEOUT_MS` — Last-workspace-activity window before an auto-provisioned workspace-role node with no active workspaces is destroy-eligible (default: `1800000`; resolved in `apps/api/src/scheduled/node-cleanup/config.ts` and enforced by `claimNodeForCleanup()` in `shared.ts`)
- `NODE_ORPHAN_IDLE_TIMEOUT_MS` — Legacy alias for `NODE_WORKSPACE_IDLE_TIMEOUT_MS` when the primary variable is unset (resolved in `apps/api/src/scheduled/node-cleanup/config.ts`)
- `NODE_CLEANUP_FAILURE_BACKOFF_MS` — Failed cleanup-candidate exclusion window (default: `3600000`)
- `NODE_UNHEALTHY_DRAIN_AFTER_MS` — Node-heartbeat silence before the managed VM cleanup sweep requests session sleep and posts a chat notice (default: `600000`)
- `NODE_UNHEALTHY_RELEASE_AFTER_MS` — Node-heartbeat silence before strict provider deletion is attempted, including with active workspaces (default: `1800000`; must exceed the drain threshold)
- `NODE_UNHEALTHY_FLEET_MAX_FRACTION` — Hold destructive unhealthy-node cleanup and escalate when this fraction of at least three managed workspace VMs is silent together (default: `0.5`)
- `NODE_UNHEALTHY_FLEET_MIN_NODES` — Minimum managed workspace VMs before the fleet-wide guard applies (default: `3`)
- `NODE_UNHEALTHY_PRESERVATION_TIMEOUT_MS` — Per-node deadline for attempting chat notices and sleep requests before release proceeds (default: `5000`)
- `NODE_UNHEALTHY_RETRY_MS` — Backoff after failed provider deletion of an unhealthy node (default: `60000`)
- `NODE_STOPPED_HANDOFF_SWEEP_BUDGET_MS` — Wall-clock budget for the stopped-node handoff phase; unstarted candidates remain eligible for the next sweep (default: `20000`)
- `NODE_STOPPED_HANDOFF_REQUEST_TIMEOUT_MS` — Per-candidate provider/DNS deadline during stopped-node handoff, capped by remaining sweep time; provider failures enter cleanup backoff (default: `5000`)
- `IDLE_CLEANUP_MAX_RESIDENCE_MS` — Maximum ProjectData idle-cleanup schedule residence before preserved/error outcomes stop re-arming and surface attention (default: `7200000`)
- `IDLE_CLEANUP_MAX_CANDIDATES_PER_SWEEP` — Maximum idle-cleanup schedules or workspace idle checks one ProjectData alarm pass takes, and maximum reporter-scoped task candidates inspected per check (default: `5`)
- `WORKSPACE_RESOURCE_TIMELINE_MAX_CHUNKS` — Max chunks the session resource timeline lists; older ones are disclosed as omitted (default: `1000`)
- `WORKSPACE_RESOURCE_ROLLUP_BUCKET_MS` / `WORKSPACE_RESOURCE_ROLLUP_MAX_BUCKETS` — Per-chunk rollup bucket width computed on upload, and the bucket cap that widens it (defaults: `60000` / `60`)
- `WORKSPACE_IDLE_TIMEOUT_MS` — Installation default for how long an active chat session's workspace can go without messages or terminal activity before ProjectData retires it, once its runtime is conclusively dead; the project Workspace Idle Timeout setting overrides it (default: `7200000`)
- `WORKSPACE_IDLE_BACKOFF_BASE_MS` — First retry delay after a ProjectData workspace-idle check finds an idle workspace it cannot retire yet: inconclusive task candidates, a live or unprovable runtime, a missing project identity, or a failed check (default: `600000`; `apps/api/src/durable-objects/project-data/workspace-idle-timeouts.ts`)
- `WORKSPACE_IDLE_BACKOFF_MAX_MS` — Maximum workspace-idle retry delay; the delay doubles from the base and resets on new activity or when the session wakes (default: `21600000`)
- `DIAGNOSIS_COMPLETED_STEP_MIN_DELAY_MS` — Minimum re-arm delay for completed diagnosis steps (default: `1000`)
- `ORCHESTRATOR_ZERO_TASK_GRACE_MS` — Grace before a zero-task mission terminalizes (default: `600000`)
- `ORCHESTRATOR_MAX_MISSION_LIFETIME_MS` — Mission lifetime backstop (default: `86400000`)
- `ORCHESTRATOR_WAIT_RECONCILE_INTERVAL_MS` — ProjectData D1 reconciliation backstop interval for active parent waits (default: `30000`)
- `ORCHESTRATOR_WAIT_MAX_CHILDREN` — Maximum same-project task IDs selected by one durable wait (default: `20`, hard ceiling: `90`)
- `ORCHESTRATOR_WAIT_MAX_ACTIVE_PER_PROJECT` — Maximum active durable parent waits per project (default: `100`)
- `ORCHESTRATOR_WAIT_MAX_DURATION_MS` — Maximum finite durable wait deadline (default: `86400000`)
- `ORCHESTRATOR_WAIT_MAX_CANDIDATES_PER_ALARM` — Maximum wait subscriptions reconciled by one ProjectData alarm (default: `10`)
- `PROJECT_DATA_TOOL_METADATA_MAX_BYTES` — Maximum stored `tool_metadata` bytes per ProjectData message before oversized tool content is stripped into bounded metadata (default: `131072`)
- `PROJECT_DATA_STORAGE_TELEMETRY_ENABLED` — Enables ProjectData `databaseSize` alarm measurement and D1 telemetry writes (default: `true`)
- `PROJECT_DATA_STORAGE_LIMIT_BYTES` — Cloudflare SQLite-backed Durable Object storage limit used for ProjectData usage classification (default: `10000000000`)
- `PROJECT_DATA_STORAGE_MEASURE_INTERVAL_MS` — Minimum interval between per-object ProjectData storage measurements (default: `3600000`)
- `PROJECT_DATA_STORAGE_ALERT_INTERVAL_MS` — Minimum interval between repeated warning/critical/degraded ProjectData storage observability alerts and cleanup target-unreachable alerts (default: `21600000`)
- `PROJECT_DATA_STORAGE_NOTICE_RATIO` — ProjectData storage usage ratio classified as `notice` (default: `0.6`)
- `PROJECT_DATA_STORAGE_WARNING_RATIO` — ProjectData storage usage ratio classified as `warning` (default: `0.8`)
- `PROJECT_DATA_STORAGE_CRITICAL_RATIO` — ProjectData storage usage ratio classified as `critical` (default: `0.9`)
- `PROJECT_DATA_STORAGE_DEGRADED_RATIO` — ProjectData storage usage ratio classified as `degraded` (default: `0.95`)
- `PROJECT_DATA_STORAGE_EMERGENCY_TARGET_RATIO` — Target usage ratio for explicit superadmin ProjectData emergency purge calls (default: `0.9`)
- `PROJECT_DATA_STORAGE_EMERGENCY_BATCH_ROWS` — Oldest `activity_events` and `acp_session_events` rows deleted per table per emergency purge batch (default: `500`)
- `PROJECT_DATA_STORAGE_EMERGENCY_MAX_BATCHES` — Maximum emergency purge batches per explicit call (default: `4`)
- `PROJECT_DATA_STORAGE_GROWTH_LOOKBACK_DAYS` — Lookback window used to estimate ProjectData bytes/day growth and days to storage limit (default: `7`)
- `PROJECT_DATA_STORAGE_TELEMETRY_LIST_LIMIT_DEFAULT` — Default row count for admin ProjectData storage telemetry and history lists (default: `50`)
- `PROJECT_DATA_STORAGE_TELEMETRY_LIST_LIMIT_MAX` — Max accepted row count for admin ProjectData storage telemetry and history lists (default: `200`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_ENABLED` — Enables automatic ProjectData cleanup that archives expandable `tool_metadata.content` payloads to private R2 before stripping them from old ProjectData message rows (default: enabled). Not sufficient alone: if `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_CUTOFF_CREATED_AT` is set, the stricter approved-manifest plan is armed and also needs `_PLAN_ID`, `_MANIFEST_KEY`, `_MANIFEST_SHA256` and all four `_MAX_TOTAL_*` ceilings, or cleanup builds no plan and logs `project_data.tool_payload_cleanup.config_refused`
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_PLAN_ID` — Immutable operator plan identifier; required with a fixed cleanup cutoff and persisted with continuation state so configuration drift fails closed (default: empty)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MANIFEST_KEY` / `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MANIFEST_SHA256` — Verified, immutable R2 target-manifest root required for a fixed cleanup plan (default: empty)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_BATCH_MANIFEST_MAX_BYTES` / `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_ROOT_MANIFEST_MAX_BYTES` — Verified approved-plan R2 manifest read/write ceilings, included in exact-plan fingerprints (defaults: `2000000` / `1000000`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_TOTAL_ROWS` / `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_TOTAL_BYTES` / `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_TOTAL_R2_OPERATIONS` / `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_TOTAL_WALL_TIME_MS` — Hard cumulative approval ceilings required for a fixed cleanup plan; exact execution transactionally charges the full per-pass R2-operation and wall-time allowances before external work, without refunds (default: empty)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_PROJECT_IDS` — Optional comma-separated project allowlist for automatic cleanup; empty preserves cleanup eligibility for every project, while an emergency rollout can scope elevated budgets to exact projects (default: empty)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_CUTOFF_CREATED_AT` — Optional fixed exclusive tool-message creation cutoff in epoch milliseconds; when set, an immutable plan ID and exactly one allowlisted project are required, and malformed/future values fail closed (default: empty, retention-derived cutoff)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_TRIGGER_RATIO` — ProjectData storage usage ratio that starts automatic tool payload archival cleanup even before the retention cadence is due (default: `0.8`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_TARGET_RATIO` — ProjectData storage usage ratio below which automatic tool payload cleanup stops (default: `0.75`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_BATCH_ROWS` — Maximum eligible tool-message candidates processed by one automatic cleanup alarm batch; ordinary candidate selection may examine a larger physical row window bounded by `PROJECT_DATA_STORAGE_RELIEF_MEASURE_MAX_BATCH_ROWS` (default: `500`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_BATCH_BYTES` — Legacy `tool_metadata` read budget for one automatic cleanup pass; an ordinary retention pass may admit its first oversized candidate up to the archive metadata ceiling to advance the cursor, while a fixed exact plan fails closed unless its per-row ceiling fits this budget (default: `2097152`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_ROW_BYTES` — Hard maximum single legacy `tool_metadata` row read by cleanup; an approved emergency plan must explicitly raise it to include larger manifest targets, without exceeding `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_MAX_METADATA_BYTES` (default: `1048576`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MIN_SESSION_AGE_DAYS` — Legacy terminal-session age guard retained for storage telemetry compatibility; tool payload archival uses `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_RETENTION_DAYS` (default: `7`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_RECHECK_MS` — Delay before the next automatic cleanup alarm batch when more candidates remain (default: `86400000`, daily)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_MAX_SESSIONS_PER_ALARM` — Deprecated legacy terminal-session cleanup knob retained for env compatibility; archival cleanup scans tool-message rows directly and is bounded by rows, bytes, and wall time instead (default: `25`)
- `PROJECT_DATA_TOOL_PAYLOAD_CLEANUP_WALL_TIME_MS` — Absolute wall-clock deadline shared by candidate processing and every R2 write/read-back operation in one cleanup pass (default: `20000`)
- `PROJECT_DATA_TOOL_PAYLOAD_MANUAL_CLEANUP_MAX_BATCH_ROWS` — Hard row cap for one explicit superadmin manual ProjectData tool payload archival cleanup pass (default: `500`)
- `PROJECT_DATA_TOOL_PAYLOAD_MANUAL_CLEANUP_MAX_BATCH_BYTES` — Maximum configurable legacy `tool_metadata` read budget for one explicit manual pass; the ordinary-path first-row exception still applies unless a fixed exact plan binds the row ceiling within this budget (default: `2097152`)
- `PROJECT_DATA_TOOL_PAYLOAD_MANUAL_CLEANUP_MAX_WALL_TIME_MS` — Hard wall-clock cap for one explicit manual cleanup pass (default: `20000`)
- `PROJECT_DATA_TOOL_PAYLOAD_MANUAL_CLEANUP_RECHECK_MS` — Persisted project-scoped cooldown after an explicit manual cleanup pass (default: `86400000`, daily)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_RETENTION_DAYS` — Message age before expandable tool payload JSON may be archived to private R2 and stripped from the ProjectData DO (default: `5`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_INTERVAL_MS` — Cadence for the retention-driven ProjectData tool payload archival scan (default: `86400000`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_R2_PREFIX` — Private R2 prefix used for archived ProjectData tool payload JSON objects (default: `project-data/tool-payloads`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_WRITE_TIMEOUT_MS` — Per-R2 write/read-back verification timeout for automatic archival cleanup; timeout leaves the original payload in ProjectData and defers retry (default: `5000`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_MAX_OPERATIONS` — Maximum R2 PUT, GET, and body-read operations reserved by one cleanup pass (default: `1500`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_RETRY_DELAY_MS` — Row-level retry deferral after retryable archive/write failures (default: `300000`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_CHUNK_BYTES` — R2 chunk size used when archiving legacy tool payload metadata larger than one archive object slice (default: `524288`)
- `PROJECT_DATA_TOOL_PAYLOAD_ARCHIVE_MAX_METADATA_BYTES` — Absolute bounded read cap for legacy oversized tool payload metadata; larger rows fail closed and remain in ProjectData (default: `1900000`)
- `PROJECT_DATA_STORAGE_RELIEF_MEASURE_BATCH_ROWS` — Default row budget for superadmin ProjectData relief measurement slices (default: `500`)
- `PROJECT_DATA_STORAGE_RELIEF_MEASURE_MAX_BATCH_ROWS` — Maximum physical row window accepted for superadmin/preflight relief measurements and examined by one ordinary cleanup candidate-selection slice (default: `5000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_ENABLED` — Enables the scheduled, read-only, fixed-cutoff ProjectData tool-payload relief preflight; requires the exact plan, project, and cutoff variables below (default: disabled)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_PLAN_ID` — Immutable operator plan identifier used to resume one preflight without mixing evidence from another run (default: empty)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_PROJECT_ID` — Exact ProjectData project targeted by the enabled preflight (default: empty)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_CUTOFF_CREATED_AT` — Fixed exclusive tool-message creation cutoff in epoch milliseconds; required while preflight is enabled (default: empty)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_BATCH_ROWS` — Maximum physical `chat_messages` rowid-window rows examined by one preflight slice before eligibility filtering (default: `5000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_INTERVAL_MS` — Persisted minimum interval between preflight slices (default: `300000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_MAX_BATCHES` — Overall claimed-attempt ceiling; an incomplete successful scan becomes truncated at the ceiling, while the final failed attempt becomes failed (default: `100`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_MAX_ROWS` — Overall physical `chat_messages` rows-examined ceiling for one preflight plan before eligibility filtering (default: `500000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_MAX_BYTES` — Overall projected net reclaimable-byte evidence ceiling for one preflight plan (default: `2000000000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_LEASE_MS` — D1 claim lease that prevents overlapping scheduled slices and must exceed the wall-time budget by the configured lease margin (default: `60000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_WALL_TIME_MS` — Absolute per-slice deadline shared by the bounded ProjectData measurement and verified R2 manifest writes/read-backs; failures retain the prior cursor and fail closed for retry (default: `20000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_SLICES_PER_RUN` — Maximum number of sequential, separately leased preflight slices in one scheduled invocation; later slices bypass only the persisted cadence for that invocation (default: `1`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_RUN_WALL_TIME_MS` — Aggregate invocation admission budget used to decide whether another full sequential slice can start; must exceed the per-slice wall-time budget by the return margin (default: `25000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_LEASE_MARGIN_MS` — Required lease headroom above the per-slice wall-time budget (default: `5000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_RETURN_MARGIN_MS` — Required return headroom inside measurement, slice, and aggregate run budgets (default: `500`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_MEASUREMENT_WALL_TIME_MS` — Explicit ProjectData measurement budget within one slice; plus the return margin it must not exceed the slice wall budget (default: `10000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_MAX_STATE_BYTES` — Combined D1 JSON byte ceiling for accumulated session and target-batch proof state in one preflight row (default: `1750000`)
- `PROJECT_DATA_STORAGE_RELIEF_PREFLIGHT_ERROR_MAX_LENGTH` — Character ceiling for a persisted preflight failure diagnostic (default: `1000`)
Implementation of the storage-safety, cleanup and preflight behavior documented above:
`resolveStorageSafetyConfig()` (`apps/api/src/durable-objects/project-data/storage-safety.ts`)
validates every value and fails closed on a malformed one;
`runProjectDataToolPayloadCleanup()` / `scanToolPayloadCleanupBatch()`
(`.../tool-payload-cleanup.ts`) apply the row/byte/wall-time and approved-plan budgets;
`runProjectDataManualToolPayloadCleanup()` (`.../tool-payload-manual-cleanup.ts`) owns the
manual slice, its fingerprint and its cooldown;
`measureProjectDataStorageReliefSlice()` (`.../storage-relief-measurement.ts`) owns the
read-only measurement, cursor and byte/deadline bounds; and
`runProjectDataStorageReliefPreflight()`
(`apps/api/src/scheduled/project-data-storage-relief-preflight.ts`) owns the lease, the
per-slice and per-run admission budgets, and the verified R2 manifest writes.
- `PROJECT_DATA_MATERIALIZATION_PAGE_ROWS` — Tokens read into memory by one chat-search materialization `SELECT`, bounding Durable Object isolate memory (default: `500`)
- `PROJECT_DATA_MATERIALIZATION_MAX_ROWS_PER_PASS` — Tokens one chat-search materialization pass indexes before leaving the rest to the next pass (default: `5000`)
- `PROJECT_DATA_MATERIALIZATION_MAX_GROUP_CHARS` — Grouped-row size past which a continuing run starts a new row instead of rewriting the accumulated content (default: `65536`)
- `PROJECT_DATA_MATERIALIZATION_SWEEP_LIMIT` — Sessions indexed by one chat-search materialization backfill call (default: `50`)
- `PROJECT_DATA_MATERIALIZATION_SWEEP_SCAN_LIMIT` — Sessions examined by one chat-search materialization backfill call (default: `500`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_ENABLED` — Production-disabled switch for cleanup of old terminal-session grouped message rows and their external-content FTS entries (default: disabled)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_TRIGGER_RATIO` — ProjectData usage ratio that starts grouped/FTS derived-data cleanup when explicitly enabled (default: `0.9`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_TARGET_RATIO` — ProjectData usage ratio below which grouped/FTS cleanup stops (default: `0.85`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_BATCH_SESSIONS` — Maximum terminal sessions cleaned by one grouped/FTS canary slice (default: `2`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_BATCH_ROWS` — Maximum grouped message rows deleted by one grouped/FTS canary slice (default: `1000`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_BATCH_BYTES` — Maximum grouped message content bytes deleted by one grouped/FTS canary slice (default: `4194304`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_MIN_SESSION_AGE_DAYS` — Minimum terminal-session age before grouped/FTS derived rows may be cleaned (default: `7`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_RECHECK_MS` — Delay before the next grouped/FTS cleanup slice when more candidates remain (default: `300000`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_WALL_TIME_MS` — Soft wall-clock budget for one grouped/FTS cleanup slice (default: `5000`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_WALL_UNSAFE_RATIO` — Refuses grouped/FTS cleanup writes when the object is too close to the configured storage limit (default: `0.98`)
- `PROJECT_DATA_GROUPED_FTS_CLEANUP_WEAK_RECLAIM_BYTES` — Stops grouped/FTS cleanup if a slice deletes rows but `databaseSize` does not drop by at least this many bytes (default: `1`)
- `PROJECT_DATA_GROUPED_FTS_WALL_RECOVERY_MAX_ROWS` — Ceiling on grouped rows one superadmin grouped/FTS wall-recovery call may prune (default: `10000`)
- `PROJECT_DATA_GROUPED_FTS_WALL_RECOVERY_MAX_BYTES` — Ceiling on grouped content bytes one grouped/FTS wall-recovery call may prune (default: `33554432`)
- `PROJECT_DATA_GROUPED_FTS_WALL_RECOVERY_MAX_SESSIONS` — Ceiling on terminal sessions one grouped/FTS wall-recovery call may consider, and on its `skipSessionIds` length (default: `500`)
- `PROJECT_DATA_GROUPED_FTS_WALL_RECOVERY_TRANSACTION_ROWS` — Grouped rows pruned per wall-recovery transaction (default: `500`)
- `PROJECT_DATA_GROUPED_FTS_WALL_RECOVERY_TRANSACTION_BYTES` — Grouped content bytes held in memory per wall-recovery transaction (default: `8388608`)
- `PROJECT_DATA_EVENT_LOG_CLEANUP_ENABLED` — Enables automatic deletion of old low-value terminal-session `activity_events` and terminal ACP event history when storage remains above the cleanup target (default: enabled)
- `PROJECT_DATA_EVENT_LOG_CLEANUP_BATCH_ROWS` — Maximum terminal `activity_events` rows and terminal `acp_session_events` rows deleted per automatic cleanup alarm batch (default: `500`)
- `PROJECT_DATA_EVENT_LOG_CLEANUP_MIN_SESSION_AGE_DAYS` — Minimum terminal-session age before automatic event-log cleanup may delete its activity/ACP event history (default: `7`)
- `PROJECT_DATA_EVENT_LOG_CLEANUP_RECHECK_MS` — Delay before the next terminal event-log cleanup alarm batch when more candidates remain (default: `86400000`, daily)
- `PROJECT_DATA_ARCHIVE_SHARDING_ENABLED` — Production-disabled switch for exact archive read routing (default: disabled)
- `PROJECT_DATA_ARCHIVE_COMPACT_ENABLED` — Write new archives as compact SQLite + compressed R2; existing formats remain readable. Default: `false`.
- `PROJECT_DATA_ARCHIVE_DAILY_WRITE_BUDGET` — Installation-wide daily allowance of ESTIMATE UNITS (not billed rows) for compact migration attempts; 0 pauses admission. Also the source of the per-candidate selection ceiling, so lowering it automatically narrows which sessions a sweep selects (default: `250000`, shipped: `2400000`, tripled with the cadence on 2026-09-27). Migrations/day ceiling is `allowance / (1000 + factor x units-per-session)`; check it against the sweep cadence, because whichever is smaller is what actually runs. The GitHub `production` Environment pins this var, so change it there in lockstep.
- `PROJECT_DATA_ARCHIVE_WRITE_ESTIMATE_FACTOR` — Safety multiplier on the row inventory `estimateArchiveWrites` already counts (rows, archived tool payloads, grouped rows, 512-byte units of grouped FTS text, source deletion included) — not an independent amplification factor. Default: `32`, shipped: `2`. Production measurement 2026-09-14: ~65,001 estimated writes per migration from two budget samples an hour apart (~8,000 inventory units at the then-shipped factor 8), against 5,950-9,823 DO rows actually billed across four isolated ticks, so billed/unit spans 0.74-1.23 and 2 exceeds the worst case by ~63%. The previous 8 capped the sweep at 12 migrations/day when its hourly cadence allowed 24. Small sample; re-measure before retuning.
- `PROJECT_DATA_ARCHIVE_R2_TIMEOUT_MS` — R2 I/O deadline shared across each compact read/export/seal operation, or one chunk write, in milliseconds. Default: `10000`.
- `PROJECT_DATA_ARCHIVE_BUDGET_RECEIPT_RETENTION_MS` — Unused reservation receipt retention; minimum one UTC budget day. Default: `604800000` (7 days).
- `PROJECT_DATA_ARCHIVE_BUDGET_RECEIPT_CLEANUP_LIMIT` — Maximum expired receipts removed per unused release; zero disables cleanup. Default: `100`.
- `PROJECT_DATA_ARCHIVE_GLOBAL_SWEEP_ENABLED` — Separate production-disabled switch for the unscoped scheduled archive-sharding sweep; enabling exact routing alone does not run global migration (default: disabled)
- `PROJECT_DATA_ARCHIVE_GLOBAL_SWEEP_INTERVAL_MS` — Persisted cadence gate between unscoped scheduled archive-sharding sweeps; the Worker scheduled handler wakes every five minutes and claims the cadence row only when it is due. The code fallback is daily; the checked-in `wrangler.toml` ships 18 minutes, which lands a claim on the fourth five-minute tick (occasionally the fifth when the archive step starts over two minutes earlier than at the previous claim). Each claim can migrate zero or more sessions (usually one, because a real candidate outlasts the `PROJECT_DATA_ARCHIVE_WALL_TIME_MS` gate checked between candidates), subject to the per-sweep session and message budgets and the daily write budget, so cadence alone does not set the drain rate (default: `86400000`, shipped: `1080000`)
- `PROJECT_DATA_ARCHIVE_SHARD_COUNT` — Deterministic archive-shard fanout used when assigning terminal sessions to ProjectData archive Durable Objects (default: `128`)
- `PROJECT_DATA_ARCHIVE_SWEEP_PROJECTS` — Maximum projects selected by one archive-sharding cron pass (default: `1`)
- `PROJECT_DATA_ARCHIVE_SWEEP_SESSIONS` — Hard ceiling on sessions (in-flight plus new candidates) one archive-sharding pass may FENCE and process; a candidate the write budget refuses opens no fence and consumes no slot (default: `10`, shipped: `8`)
- `PROJECT_DATA_ARCHIVE_SWEEP_MESSAGE_BUDGET` — Cumulative `session_summaries.message_count` of NEW candidates one pass may journal, largest first. An upper bound only: the per-candidate ceiling is the smaller of this and the ceiling derived from `PROJECT_DATA_ARCHIVE_DAILY_WRITE_BUDGET` (default: `20000`, shipped: `10000`)
- `PROJECT_DATA_ARCHIVE_SWEEP_UNIT_OVERHEAD_PERCENT` — Assumed percentage by which a session's write estimate exceeds its `message_count` (archived tool payloads, grouped rows, FTS units); reserved as headroom when deriving the selection ceiling from the daily allowance (default: `100`, shipped: `100`)
- `PROJECT_DATA_ARCHIVE_SWEEP_FALLTHROUGH_DEPTH` — Spare candidates read beyond the pass's session slots, so a candidate the daily write budget refuses can descend to a smaller one instead of ending the pass with nothing migrated (default: `4`, shipped: `4`)
- `PROJECT_DATA_ARCHIVE_BUDGET_STALL_ALERT_SWEEPS` — Consecutive sweeps that may migrate nothing because every candidate exceeded the whole daily allowance before the cadence row reports `partial` instead of `succeeded`; an exhausted daily pool is normal backpressure and is not counted (default: `3`, shipped: `3`)
- `PROJECT_DATA_ARCHIVE_SESSION_GRACE_MS` — Minimum terminal-session age before archive-sharding may consider a session (default: `604800000`)
- `PROJECT_DATA_ARCHIVE_PRECOPY_REFUSAL_RETRY_MS` — How long a session the root object refused at prepare (a pre-copy eligibility invariant such as `active_session_state`, recorded as a `frozen`/`precopy_refused` journal with the location returned to `root`) stays out of unscoped sweep selection; an operator canary that names the session bypasses it; values below `60000` fall back to the default so a stray value cannot disable the anti-thrash window (default: `604800000`)
- `PROJECT_DATA_ARCHIVE_FAILED_RETRY_DELAY_MS` — Minimum age of a `failed` archive journal before an unscoped sweep reclaims it, so `PROJECT_DATA_ARCHIVE_POISON_AFTER_ATTEMPTS` spans hours rather than consecutive cadence ticks; `0` disables the delay and a session-scoped canary bypasses it (default: `3600000`)
- `PROJECT_DATA_ARCHIVE_CHUNK_ROWS` — Maximum rows exported in one archive chunk (default: `500`)
- `PROJECT_DATA_ARCHIVE_CHUNK_BYTES` — Maximum bytes exported in one archive chunk, clamped below Cloudflare's 32MiB RPC ceiling (default: `16777216`)
- `PROJECT_DATA_ARCHIVE_HASH_PAGE_ROWS` — Rows read per statement while streaming a session's terminal-version hash and grouped-row loops inside the ProjectData DO; bounds object memory by page size instead of session size (default: `500`)
- `PROJECT_DATA_ARCHIVE_LEASE_MS` — D1 CAS journal lease duration for archive-sharding work (default: `300000`)
- `PROJECT_DATA_ARCHIVE_WALL_TIME_MS` — Soft wall-clock budget for one archive-sharding cron pass, checked between candidates (a single large session may run past it). Code fallback 5 s; the checked-in `wrangler.toml` ships 10 s after the billing firebreak (default: `5000`, shipped: `10000`)
- `PROJECT_DATA_ARCHIVE_ROLLOUT_LIST_LIMIT_DEFAULT` — Default row limit for superadmin archive-sharding rollout inspection and failed/poisoned/frozen migration list endpoints (default: `25`)
- `PROJECT_DATA_ARCHIVE_ROLLOUT_LIST_LIMIT_MAX` — Maximum accepted row limit for superadmin archive-sharding rollout inspection endpoints (default: `100`)
- `PROJECT_DATA_ARCHIVE_FROZEN_INTENT_INSPECTION_LIMIT_DEFAULT` — Default row limit for frozen-intent detail inspection; each row may perform source and target ProjectData DO RPCs (default: `5`)
- `PROJECT_DATA_ARCHIVE_FROZEN_INTENT_INSPECTION_LIMIT_MAX` — Maximum accepted frozen-intent detail inspection limit; configured values are hard-clamped to 10 to cap hot-DO fan-out (default: `10`)
- `PROJECT_DATA_ARCHIVE_MANUAL_CANARY_MAX_SESSIONS` — Maximum sessions selected by one scoped manual archive-sharding canary; dry-run remains flag-independent, non-dry requires exact archive routing enabled (default: `5`)
- `PROJECT_DATA_ARCHIVE_MANUAL_CANARY_MAX_WALL_TIME_MS` — Maximum wall-clock budget accepted by the scoped manual archive-sharding canary endpoint (default: `15000`)
- `PROJECT_DATA_ARCHIVE_ROLLOUT_WARNING_EXAMPLES_MAX` — Maximum malformed-row warning examples returned by archive rollout read/list endpoints (default: `5`)
- `PROJECT_DATA_ARCHIVE_ROLLOUT_WARNING_REASON_MAX_LENGTH` — Maximum characters per malformed-row warning reason returned by archive rollout read/list endpoints (default: `300`)
- `PROJECT_DATA_ARCHIVE_POISON_AFTER_ATTEMPTS` — Failed archive-sharding attempts before the migration is poisoned and the project circuit breaker opens (default: `3`)
- `PROJECT_DATA_ARCHIVE_R2_PREFIX` — Private R2 prefix for terminal-session archive recovery chunks and manifests (default: `project-data/session-archives`)
- `PROJECT_DATA_ARCHIVE_SEARCH_MAX_OWNERS` — Archive-owner batch size processed by one project-wide search continuation step; repeat the signed continuation until the frozen inventory is exhausted (default: `4`, max: `64`)
- `PROJECT_DATA_ARCHIVE_SEARCH_CONCURRENCY` — Maximum archive-owner Durable Object searches executed concurrently within one continuation step (default: `4`, max: `16`)
- `PROJECT_DATA_ARCHIVE_SEARCH_REPAIR_SESSIONS` — Maximum incomplete compact-archive sessions repaired per owner search call (default: `1`, max: `16`)
- `PROJECT_DATA_ARCHIVE_SEARCH_REPAIR_CHUNKS` — Maximum compact R2 chunks consumed per session repair step (default: `1`, max: `64`)
- `PROJECT_DATA_ARCHIVE_SEARCH_CONTINUATION_TTL_MS` — Fixed lifetime of a signed project-wide archive-search continuation; continuation does not extend it (default: `900000`, max: `86400000`)
- `PROJECT_DATA_ARCHIVE_SEARCH_CURSOR_MAX_BYTES` — Maximum encoded byte size accepted or emitted for a signed archive-search continuation (default: `1048576`, max: `4194304`)
- `PROJECT_DATA_ARCHIVE_SEARCH_ERROR_LIMIT` — Maximum entries retained independently in each public execution- and index-error collection (default: `20`, max: `100`)
- `PROJECT_DATA_SEARCH_FTS_CANDIDATE_LIMIT` — Newest full-text matches bm25-ranked per ProjectData message search; when more match, results report `rootSearch.ftsCandidatesTruncated` (default: `2000`)
- `PROJECT_DATA_SEARCH_FTS_SCAN_LIMIT` — Full-text index entries inside a session's rowid span that a session-scoped search examines, newest first, to collect that session's newest matches; never smaller than `PROJECT_DATA_SEARCH_FTS_CANDIDATE_LIMIT` (default: `20000`)
- `PROJECT_DATA_SEARCH_KEYWORD_SCAN_ROW_LIMIT` — Newest raw messages the keyword fallback scans for not-yet-indexed text; older rows are reported as `rootSearch.keywordScanTruncated` (default: `50000`)
- `PROJECT_DATA_ALARM_SECTION_GATING_ENABLED` — Run only the ProjectData alarm sections that are due each tick; `false` runs every section every tick (default: `true`)
- `PROJECT_DATA_ALARM_FULL_RUN_INTERVAL_MS` — Maximum interval between ProjectData alarm ticks that run every section regardless of due times (default: `900000`)
- `PROJECT_DATA_ALARM_DUE_TOLERANCE_MS` — A ProjectData alarm section due within this many ms of the tick runs in that tick (default: `2000`)
- `PROJECT_DATA_ALARM_SLOW_SECTION_MS` — ProjectData alarm sections at or above this wall time log `project_data.alarm.section_slow` (default: `1000`)
- `PROJECT_EVENT_MAX_ACTIVE_SUBSCRIPTIONS_PER_PROJECT` — Maximum active durable event subscriptions in one ProjectData object (default: `200`)
- `PROJECT_EVENT_FILTER_MAX_VALUES_PER_FIELD` — Maximum exact/set values accepted for one v1 event filter field (default: `20`)
- `PROJECT_EVENT_FILTER_MAX_MATCH_KEYS` — Maximum deterministic match keys compiled for one event subscription (default: `100`)
- `PROJECT_EVENT_FILTER_MAX_STRING_BYTES` — Maximum bytes for event source/type/subject, filter values, delivery keys, fingerprints, and event idempotency strings (default: `160`)
- `PROJECT_EVENT_METADATA_MAX_BYTES` — Maximum normalized event metadata JSON bytes stored in ProjectData (default: `8192`)
- `PROJECT_EVENT_METADATA_MAX_DEPTH` — Maximum nesting depth for normalized event metadata (default: `4`)
- `PROJECT_EVENT_METADATA_MAX_KEYS` — Maximum total object keys in normalized event metadata (default: `64`)
- `PROJECT_EVENT_METADATA_MAX_ARRAY_ITEMS` — Maximum array items in normalized event metadata (default: `64`)
- `PROJECT_EVENT_DISPLAY_MAX_BYTES` — Maximum deterministic untrusted event display JSON bytes (default: `4096`)
- `PROJECT_EVENT_DISPLAY_MAX_LABELS` — Maximum labels accepted in event display data (default: `12`)
- `PROJECT_EVENT_RAW_PAYLOAD_REF_MAX_BYTES` — Maximum optional raw-payload reference JSON bytes; raw webhook bodies are not persisted in ProjectData (default: `512`)
- `PROJECT_EVENT_REASON_MAX_BYTES` — Maximum diagnostic reason bytes on subscriptions, batches, and attempts (default: `1024`)
- `PROJECT_EVENT_MAX_MATCHES_PER_EVENT` — Maximum durable subscription matches written for one admitted normalized event (default: `100`)
- `PROJECT_EVENT_DELIVERY_BATCH_MAX_EVENTS` — Maximum event matches in one durable delivery batch (default: `50`)
- `PROJECT_EVENT_DELIVERY_ATTEMPT_MAX_PER_BATCH` — Maximum recorded delivery attempts for one event delivery batch (default: `10`)
- `PROJECT_EVENT_SCHEDULE_MAX_SCHEDULES` — Maximum active schedules per project (default: `128`)
- `PROJECT_EVENT_SCHEDULE_MAX_WATCHES` — Maximum active or paused standing watches per project (default: `64`)
- `PROJECT_EVENT_SCHEDULE_MAX_RETAINED_SCHEDULES` — Maximum retained schedule records of all states per project; cancelling does not free retained capacity (default: `4096`)
- `PROJECT_EVENT_SCHEDULE_MAX_RETAINED_WATCHES` — Maximum retained watch records of all states per project; revoking does not free retained capacity (default: `256`)
- `PROJECT_EVENT_SCHEDULE_PROMPT_MAX_BYTES` — Maximum scheduled action prompt size in UTF-8 bytes; task submission also enforces MAX_TASK_MESSAGE_LENGTH (default: `32768`)
- `PROJECT_EVENT_SCHEDULE_MAX_HORIZON_MS` — Maximum future scheduling horizon (30 days) (default: `2592000000`)
- `PROJECT_EVENT_SCHEDULE_LATE_GRACE_MS` — Maximum late admission grace after due time (default: `86400000`)
- `PROJECT_EVENT_SCHEDULE_DELIVERY_TTL_MS` — Maximum durable message lifetime, also bounded by schedule expiry (default: `86400000`)
- `PROJECT_EVENT_SCHEDULE_SWEEP_BATCH_SIZE` — Maximum schedule or watch candidates processed per alarm pass (default: `20`)
- `PROJECT_EVENT_SCHEDULE_CLAIM_LEASE_MS` — Finite admission/submission claim lease (default: `60000`)
- `PROJECT_EVENT_SCHEDULE_RETRY_BASE_MS` — Retry and accepted execution reconciliation interval (default: `30000`)
- `PROJECT_EVENT_SCHEDULE_MAX_ATTEMPTS` — Maximum uncertain task submission attempts (default: `8`)
- `PROJECT_EVENT_SCHEDULE_MAX_DEFERRAL_MS` — Maximum deferral before a new task may initially start (default: `86400000`)
- `PROJECT_EVENT_WATCH_COOLDOWN_MIN_MS` — Minimum time between standing watch executions (default: `60000`)
- `PROJECT_EVENT_WATCH_MAX_EXECUTIONS` — Maximum configurable executions per standing watch (default: `100`)
- `PROJECT_EVENT_WATCH_MAX_CONCURRENT` — Maximum configurable concurrent actions per standing watch (default: `3`)
- `PROJECT_EVENT_WAKE_ENABLED` — Enables same-chat ProjectData event wake materialization for v2 `existing_session_prompt` and `runtime_interrupt` subscriptions; unset or `false` leaves event delivery pull-only (default: `false`)
- `PROJECT_EVENT_WAKE_MATERIALIZATION_MIN_ALARM_DELAY_MS` — Minimum delay before a ProjectData alarm retries wake materialization after due matches are present (default: `1000`)
- `PROJECT_EVENT_WAKE_MATERIALIZATION_BACKOFF_BASE_MS` — Initial persisted backoff for a failed event-wake materialization or retention alarm phase (default: `5000`)
- `PROJECT_EVENT_WAKE_MATERIALIZATION_BACKOFF_MAX_MS` — Maximum persisted backoff for repeated event-wake materialization or retention alarm failures (default: `300000`)
- `PROJECT_EVENT_WAKE_PROMPT_TTL_MS` — Hard lifetime for queued same-chat event wake prompts before physical delivery is rejected (default: `86400000`)
- `PROJECT_EVENT_WAKE_READ_GRACE_MS` — Read/ack grace retained on accepted event-wake batches after natural subscription expiry (default: `86400000`)
- `PROJECT_EVENT_WAKE_TARGET_COOLDOWN_MS` — Minimum delay before trying another event wake for a chat that already has a live prompt-queue event batch (default: `30000`)
- `PROJECT_EVENT_WAKE_SUBSCRIPTION_COOLDOWN_MS` — Per-subscription cooldown after a wake batch is queued (default: `30000`)
- `PROJECT_EVENT_WAKE_SUBSCRIPTION_LIFETIME_MS` — Maximum delivery lifetime for an event subscription from creation (default: `86400000`)
- `PROJECT_EVENT_WAKE_MAX_PER_SUBSCRIPTION` — Maximum wake deliveries per subscription over its lifetime (default: `50`)
- `PROJECT_EVENT_CHANNEL_MAX_CHANNELS` — Maximum catalog generations per project (default: `128`)
- `PROJECT_EVENT_CHANNEL_MESSAGE_MAX_BYTES` — Maximum channel message UTF-8 bytes, also subject to canonical metadata limits (default: `4096`)
- `PROJECT_EVENT_CHANNEL_NAME_MAX_BYTES` — Maximum channel name bytes (default: `64`)
- `PROJECT_EVENT_CHANNEL_PUBLISH_WINDOW_MS` — Per-project fixed publish window in milliseconds (default: `60000`)
- `PROJECT_EVENT_CHANNEL_PUBLISH_MAX_PER_WINDOW` — Maximum newly committed channel publishes per project window; retained replays do not consume quota (default: `120`)
- `PROJECT_EVENT_CHANNEL_CURSOR_TTL_MS` — History cursor and unfinished catch-up lifetime in milliseconds; continuation never extends it (default: `3600000`)
- `PROJECT_EVENT_CHANNEL_CATALOG_IDLE_TTL_MS` — Minimum idle time before reclaiming an empty catalog generation without live catch-up (default: `2592000000`)
- `PROJECT_EVENT_LIST_LIMIT` — Default ProjectData event-subscription list/status page size (default: `50`)
- `PROJECT_EVENT_LIST_MAX` — Maximum ProjectData event-subscription list/status page size (default: `200`)
- `PROJECT_EVENT_SUBSCRIPTION_EVENT_CURSOR_MAX_LENGTH` — Maximum opaque `list_subscription_events` cursor length accepted by ProjectData pull delivery (default: `512`)
- `PROJECT_EVENT_RECENT_STATUS_LIMIT` — Maximum rows returned per section in recent event-subscription status inspection (default: `50`)
- `PROJECT_EVENT_RETENTION_DAYS` — Retention window for terminal ProjectData event-subscription records (default: `30`)
- `PROJECT_EVENT_RETENTION_BATCH_ROWS` — Maximum old rows pruned per event-subscription retention category per pass (default: `500`)
Absent operational brake keys and KV read errors mean enabled. This fail-open
behavior preserves availability and intentionally differs from the fail-closed
trials entitlement switch.
GitHub Environment variables for the scheduled Durable Object monitor:
- `DO_WALL_TIME_SCRIPT_NAMES` — Optional wall-time/rate service filter and fallback cron-liveness target
- `DO_INVOCATION_RATE_REGRESSION_RATIO` — Recent-versus-baseline invocation-rate threshold (default: `2`)
- `DO_CRON_LIVENESS_MAX_AGE_HOURS` — Maximum allowed age of `cron.completed` (default: `3`)
- `DO_CRON_LIVENESS_SCRIPT_NAMES` — Explicit API Worker service target for cron liveness; the GitHub workflow derives both script-name filters from `RESOURCE_PREFIX` and the selected stack when unset
- `DO_CRON_LIVENESS_ENDPOINT` — Optional Workers Observability query endpoint override passed by the monitor workflow
The monitor's `CF_API_TOKEN` GitHub Environment secret must include the
Cloudflare `Workers Observability Write` permission. Despite the permission
name, Cloudflare documents it for the supported telemetry query endpoint used
by the read-only cron-liveness check.
### Human Input and Web Push
- `HUMAN_INPUT_TIMEOUT_MS` — Initial needs-input response window (default: `7200000`)
- `HUMAN_INPUT_ESCALATION_FRACTIONS` — Comma-separated reminder points within the initial window (default: `0.25,0.75`)
- `HUMAN_INPUT_UNDELIVERED_GRACE_MS` — Extension when no push delivery was confirmed (default: `7200000`)
- `HUMAN_INPUT_MAX_WAIT_MS` — Hard maximum needs-input marker lifetime (default: `86400000`)
- `VAPID_PRIVATE_KEY`, `VAPID_PUBLIC_KEY`, `VAPID_SUBJECT` — Deployment-generated Worker secrets for Web Push authentication and browser subscription
- `WEB_PUSH_TTL_SECONDS` — Push-service message TTL (default: `86400`)
- `WEB_PUSH_VAPID_TTL_SECONDS` — VAPID authorization-token lifetime (default: `43200`)
- `WEB_PUSH_DELIVERY_TIMEOUT_MS` — Per-attempt delivery timeout (default: `10000`)
- `WEB_PUSH_DELIVERY_BUDGET_MS` — Total delivery/fan-out budget kept below the Worker `waitUntil()` lifetime (default: `25000`)
- `WEB_PUSH_FANOUT_CONCURRENCY` — Maximum endpoint deliveries processed concurrently (default: `8`)
- `WEB_PUSH_MAX_ATTEMPTS` — Bounded transient delivery attempts (default: `3`)
- `WEB_PUSH_MAX_RETRY_AFTER_SECONDS` — Maximum honored `Retry-After` delay (default: `30`)
- `WEB_PUSH_MAX_PAYLOAD_BYTES` — Maximum unencrypted payload size (default: `3500`)
- `WEB_PUSH_FAILURE_THRESHOLD` — Consecutive failures before disabling a subscription (default: `5`)
- `WEB_PUSH_MAX_SUBSCRIPTIONS_PER_USER` — Maximum retained browser endpoints per user (default: `8`)
- `WEB_PUSH_USER_AGENT_MAX_LENGTH` — Maximum stored browser description length (default: `512`)
- `RATE_LIMIT_PUSH_SUBSCRIPTION` — Subscription mutations allowed per user per hour (default: `30`)
### Devcontainer Cache
- `DEVCONTAINER_CACHE_ENABLED` — Enables opportunistic devcontainer image caching
- `DEVCONTAINER_CACHE_REGISTRY_HOST` — Managed registry host (default: `registry.cloudflare.com`)
- `DEVCONTAINER_CACHE_REPOSITORY_PREFIX` — Prefix for generated cache repository names
- `DEVCONTAINER_CACHE_CREDENTIAL_EXPIRATION_MINUTES` — TTL for short-lived registry credentials minted by the API
### Google OAuth and GCP
- `GOOGLE_LOGIN_CLIENT_ID`, `GOOGLE_LOGIN_CLIENT_SECRET` — Optional Google user-login OAuth fallback. Runtime `/setup` or superadmin config takes precedence. Callback: `/api/auth/callback/google`.
- `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` — Optional, independent infrastructure OAuth fallback used only for keyless GCP/WIF setup. Runtime superadmin config takes precedence. Callbacks: `/auth/google/callback` and `/api/deployment/gcp/callback`.
- `GCP_WIF_POOL_ID`, `GCP_WIF_PROVIDER_ID`, `GCP_SERVICE_ACCOUNT_ID` — Default identifiers used by WIF setup.
- `GCP_SERVICE_ACCOUNT_JSON_MAX_BYTES` — Maximum UTF-8 service-account JSON upload/paste size (default: `65536`).
- `GCP_DEFAULT_ZONE` — Default Compute zone (default: `us-central1-a`).
- `GCP_IMAGE_FAMILY`, `GCP_IMAGE_PROJECT`, `GCP_DISK_SIZE_GB` — Compute VM image and disk defaults.
- `GCP_TOKEN_CACHE_TTL_SECONDS` — Maximum short-lived access-token cache TTL (default: `3300`; capped by the provider-returned expiry).
- `GCP_IDENTITY_TOKEN_EXPIRY_SECONDS` — WIF SAM identity-token lifetime (default: `600`).
- `GCP_OPERATION_POLL_TIMEOUT_MS` — GCP asynchronous operation timeout (default: `300000`).
- `GCP_API_TIMEOUT_MS` — Timeout for Google OAuth, IAM, and Compute verification requests (default: `30000`).
- `GCP_STS_SCOPE` — WIF STS scope (default: `https://www.googleapis.com/auth/cloud-platform`).
- `GCP_SA_IMPERSONATION_SCOPES` — Comma-separated WIF service-account impersonation scopes (default: Compute).
- `GCP_SA_TOKEN_LIFETIME_SECONDS` — WIF impersonated-token lifetime (default: `3600`).
- `GCP_STS_TOKEN_URL`, `GCP_IAM_CREDENTIALS_BASE_URL` — WIF endpoint overrides for controlled environments. They do not affect service-account JWT exchange, which always uses Google's fixed OAuth token endpoint.
- `GCP_DEPLOY_WIF_POOL_ID`, `GCP_DEPLOY_WIF_PROVIDER_ID`, `GCP_DEPLOY_SERVICE_ACCOUNT_ID` — Default identifiers for deployment WIF.
- `GCP_DEPLOY_IDENTITY_TOKEN_EXPIRY_SECONDS`, `GCP_DEPLOY_OAUTH_STATE_TTL_SECONDS`, `GCP_DEPLOY_OAUTH_TOKEN_HANDLE_TTL_SECONDS` — Deployment authorization lifetimes.
### Resource Limits
- `MAX_NODES_PER_USER` — Runtime node cap
- `MAX_AGENT_SESSIONS_PER_WORKSPACE` — Runtime session cap
- `VM_ADMISSION_CONTROL_MODE` — VM task/session admission mode for node-packing backpressure (`off`, `shadow`, `enforce`; default: `enforce`)
- `VM_ADMISSION_LEASE_TTL_MS` — Fenced VM provisioning claim lease duration (default: `1200000`)
- `VM_ADMISSION_RETRY_MIN_MS` / `VM_ADMISSION_RETRY_MAX_MS` — Bounds for retrying tasks waiting on VM capacity (defaults: `15000` / `60000`)
- `VM_ADMISSION_WAIT_TIMEOUT_MS` — Maximum visible wait for VM capacity before failing the task (default: `7200000`)
- `VM_ADMISSION_BUSY_BUILD_WAIT_TIMEOUT_MS` — Maximum bounded wait for an otherwise-eligible reusable VM node whose only blocker is an active workspace build queue (default: `1200000`)
- `VM_ADMISSION_PROVIDER_COOLDOWN_MS` — Cooldown after provider/account capacity errors such as Hetzner server limits (default: `600000`)
- `VM_ADMISSION_WAKE_BATCH_SIZE` — Maximum waiting TaskRunner DOs nudged by one capacity event (default: `25`)
- `VM_ADMISSION_DIAGNOSTIC_MESSAGE_MAX_LENGTH` — Maximum provider diagnostic message persisted on admission/capacity rows (default: `500`)
- `MAX_PROJECTS_PER_USER` — Runtime project cap
- `MAX_TASKS_PER_PROJECT` — Runtime task cap per project
- `MAX_TASK_DEPENDENCIES_PER_TASK` — Runtime dependency-edge cap per task
- `PROJECT_INVITE_TOKEN_BYTES` — Random bytes used for generated project invite link tokens (default: 32)
- `PROJECT_INVITE_DEFAULT_EXPIRY_DAYS` — Default lifetime for project invite links created without an explicit expiry (default: 7)
- `PROJECT_INVITE_MAX_EXPIRY_DAYS` — Maximum allowed project invite link lifetime (default: 30)
- `AGENT_SETTINGS_VALIDATION_LIMITS` — Optional JSON object overriding
agent-settings validation bounds for model IDs, tool lists, additional env
entries, provider display names, and OpenCode base URLs. See
`apps/api/.env.example` and `apps/www/src/content/docs/docs/guides/self-hosting.mdx` for supported keys
and defaults.
### Platform Feedback and Report an Issue
- `PLATFORM_FEEDBACK_PROJECT_ID` — Bootstrap/environment fallback for the private project that receives Report-an-Issue submissions, automated platform triage records, and incident trigger agents. The Admin → Integrations runtime setting is preferred and overrides it; no effective project hides in-app reporting and disables incident trigger sweeps.
- `PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES`, `PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT`, `PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT`, `PLATFORM_FEEDBACK_TRIAGE_EVIDENCE_LIMIT`, `PLATFORM_FEEDBACK_TRIAGE_CLAIM_TTL_MS`, `PLATFORM_FEEDBACK_TRIAGE_MAX_FAILURES`, `PLATFORM_FEEDBACK_TRIAGE_FAILURE_REASON_MAX_LENGTH`, `PLATFORM_FEEDBACK_TRIAGE_BUDGET_DEFER_MS` — Hourly platform-error grouping, budget deferral, and draft-Idea triage bounds.
- `DEBUG_AGENT_MODEL`, `DEBUG_AGENT_MAX_TURNS`, `DEBUG_AGENT_RUN_TOKEN_LIMIT`, `DEBUG_AGENT_MODEL_OUTPUT_TOKENS`, `DEBUG_AGENT_DAILY_TOKEN_LIMIT`, `DEBUG_AGENT_TOOL_RESULT_LIMIT`, `DEBUG_AGENT_TOOL_RESULT_BYTES`, `DEBUG_AGENT_MAX_WINDOW_HOURS`, `DEBUG_AGENT_TIMEOUT_MS`, `DEBUG_AGENT_HARD_DEADLINE_MS`, `DEBUG_AGENT_STALE_HEARTBEAT_MS`, `DEBUG_AGENT_RETRY_BASE_DELAY_MS`, `DEBUG_AGENT_RETRY_MAX_DELAY_MS`, `DEBUG_AGENT_STEP_MAX_RETRIES` — Automated and manual diagnosis model, turn, retry, timeout, and token-budget controls.
- `PLATFORM_FEEDBACK_INCIDENT_DISPATCH_LEASE_TTL_MS`, `PLATFORM_FEEDBACK_INCIDENT_AGENT_LEASE_TTL_MS`, `PLATFORM_FEEDBACK_INCIDENT_MAX_DISPATCH_ATTEMPTS`, `PLATFORM_FEEDBACK_INCIDENT_REOPEN_COOLDOWN_MS`, `PLATFORM_FEEDBACK_INCIDENT_RECLAIM_LIMIT`, `PLATFORM_FEEDBACK_INCIDENT_MAX_AGE_MS`, `PLATFORM_FEEDBACK_INCIDENT_STALE_SINGLETON_MAX_AGE_MS`, `PLATFORM_FEEDBACK_INCIDENT_STALE_SINGLETON_EXPIRY_BATCH_SIZE`, `PLATFORM_FEEDBACK_INCIDENT_TRIGGER_LIMIT` — Durable private incident backlog dispatch/claim/reopen/reclaim/expiry state-machine and sweep bounds.
- `PLATFORM_FEEDBACK_INCIDENT_MIN_DISPATCH_SEVERITY`, `PLATFORM_FEEDBACK_INCIDENT_MIN_DISPATCH_BATCH_SIZE`, `PLATFORM_FEEDBACK_INCIDENT_MIN_PENDING_AGE_MS`, `PLATFORM_FEEDBACK_INCIDENT_DISPATCH_RATE_WINDOW_MS`, `PLATFORM_FEEDBACK_INCIDENT_MAX_DISPATCHES_PER_TRIGGER_WINDOW` — Automatic incident dispatch eligibility, batch/age admission, and per-trigger rate-cap controls.
- `PLATFORM_FEEDBACK_INCIDENT_SUMMARY_LIMIT`, `PLATFORM_FEEDBACK_INCIDENT_EVIDENCE_REF_LIMIT`, `PLATFORM_FEEDBACK_INCIDENT_EVIDENCE_MAX_BYTES`, `PLATFORM_FEEDBACK_INCIDENT_RESOLUTION_NOTE_MAX_LENGTH` — Model-visible incident summary/evidence/resolution size limits.
- `PLATFORM_FEEDBACK_INCIDENT_AUTO_TRIGGER_ENABLED`, `PLATFORM_FEEDBACK_INCIDENT_TRIGGER_NAME`, `PLATFORM_FEEDBACK_INCIDENT_TRIGGER_TEMPLATE` — Private incident trigger auto-creation and default prompt configuration.
- `REPORT_ISSUE_TITLE_MAX_LENGTH`, `REPORT_ISSUE_DESCRIPTION_MAX_LENGTH`, `REPORT_ISSUE_CONTENT_MAX_LENGTH`, `RATE_LIMIT_REPORT_ISSUE_POST` — In-app user report truncation and rate limits.
### Pagination
- `DASHBOARD_ACTIVE_TASK_LIMIT` — Maximum active tasks returned by `/api/dashboard/active-tasks`: the most recently active (newest message, else start/submit time), newest first (default: `6`)
- `DASHBOARD_ACTIVE_TASK_CANDIDATE_LIMIT` — Active tasks read and ranked by recency before `DASHBOARD_ACTIVE_TASK_LIMIT` applies (default and maximum: `100`, the per-statement SQL bind ceiling of the per-project session lookup)
- `DASHBOARD_INACTIVE_THRESHOLD_MS` — Last-message freshness threshold for Active vs Idle on `/api/dashboard/active-tasks` (default: `900000`)
- `TASK_LIST_DEFAULT_PAGE_SIZE` — Default task/project list page size
- `TASK_LIST_MAX_PAGE_SIZE` — Maximum task/project list page size
- `CHAT_SESSION_MESSAGE_LIMIT` — Default page size for chat session message REST responses when no limit is requested — used by the 3s poll and load-more (default: 500)
- `CHAT_SESSION_MESSAGE_MAX` — Ceiling any chat session message request is clamped to; the project chat opens on the newest page, pages older history in, and never requests it (default: 50000)
- `CHAT_SESSION_DELTA_MESSAGE_LIMIT` — Default page size for forward-cursor chat delta fetches after the newest cached browser message (default: 5000)
- `MCP_ARCHIVED_TOOL_PAYLOAD_LIST_LIMIT` — Default result count for the `get_archived_tool_payloads` MCP tool (default: 10)
- `MCP_ARCHIVED_TOOL_PAYLOAD_LIST_MAX` — Maximum result count accepted by the `get_archived_tool_payloads` MCP tool (default: 50)
- `SEARCH_QUERY_MAX_LENGTH` — Maximum total UTF-8 bytes retained from idea, task, knowledge, and message search input as a DoS guard; responses disclose truncation (default: `4096`)
- `SEARCH_QUERY_MAX_TERM_LENGTH` — Maximum LIKE-safe UTF-8 bytes retained per search term; higher values clamp to SQLite's safe pattern ceiling (default: `48`)
- `SEARCH_QUERY_MAX_TERMS` — Maximum whitespace-delimited terms retained from those search inputs; higher values clamp to the safe D1 parameter ceiling and responses disclose truncation (default: `40`)
- `MCP_TRIGGER_LIST_LIMIT` — Default result count for the `list_triggers` MCP tool (default: 20)
- `MCP_TRIGGER_LIST_MAX` — Maximum result count accepted by the `list_triggers` MCP tool (default: 100)
- `MCP_INCIDENT_LIST_LIMIT` — Default result count for the private `list_incident_queue` MCP tool (default: 10)
- `MCP_INCIDENT_LIST_MAX` — Maximum result count accepted by the private `list_incident_queue` MCP tool (default: 50)
- `HETZNER_MAX_LIST_PAGES` — Maximum pages per Hetzner list request (default: 100)
### Callback Tokens
- `CALLBACK_TOKEN_EXPIRY_MS` — Lifetime of node- and workspace-scoped VM callback JWTs (default: `86400000` / 24h). Changing it does not extend tokens already issued.
- `CALLBACK_TOKEN_REFRESH_THRESHOLD_RATIO` — Fraction of a callback token's lifetime after which it may be renewed (default: `0.5`, clamped to `0.1`–`0.9`). Gates both the node token refresh in `POST /api/nodes/:id/heartbeat` and workspace token renewal in `POST /api/workspaces/:id/callback-token/renew`; a token younger than this is not re-minted.
- `RATE_LIMIT_CALLBACK_TOKEN_RENEWAL` — Authenticated workspace callback-token renewal attempts allowed per workspace per window (default: `12`). Counted atomically in D1 (`workspace_callback_token_renewal_rate_limits`) only after both proofs and the node binding pass; a healthy agent asks about once per half token lifetime.
- `RATE_LIMIT_CALLBACK_TOKEN_RENEWAL_WINDOW_SECONDS` — Window for `RATE_LIMIT_CALLBACK_TOKEN_RENEWAL` (default: `3600`).
### Timeouts
- `ORCHESTRATOR_STOP_CAS_MAX_ATTEMPTS` — Maximum task-status compare-and-set attempts after a parent hard-stops a child runtime (default: 2)
- `TASK_CALLBACK_TIMEOUT_MS` — Timeout budget for delegated-task callback processing
- `TASK_CALLBACK_RETRY_MAX_ATTEMPTS` — Retry budget for delegated-task callback processing
- `TASK_RECONCILIATION_IDLE_MS` — Idle threshold before a visible task reconciliation check-in (default: 300000)
- `TASK_RECONCILIATION_RESPONSE_DEADLINE_MS` — Response deadline after a visible task reconciliation check-in (default: 60000)
- `TASK_RECONCILIATION_PROMPT_SOFT_STALL_MS` — In-flight prompt observation threshold before SAM records a non-interrupting reconciliation event (default: 1800000)
- `TASK_RECONCILIATION_PROMPT_HARD_STALL_MS` — In-flight prompt hard-stall threshold before SAM requests prompt cancellation and retries check-in later (default: 7200000)
- `TASK_RECONCILIATION_ACTIVE_WORK_HARD_STALL_MS` — Hard ceiling for deferring an expired check-in because prompt/tool work is still active (default: 7200000)
- `TASK_RECONCILIATION_MIN_ALARM_DELAY_MS` — Minimum delay before the next reconciliation alarm can fire (default: 10000)
- `TASK_RECONCILIATION_MAX_CANDIDATES_PER_SWEEP` — Maximum D1/runtime assessments per ProjectData alarm pass (default: 5)
- `TASK_RECONCILIATION_NODE_CALL_TIMEOUT_MS` — Bounded reconciliation check-in/cancel request timeout (default: 5000)
- `TASK_RECONCILIATION_CANDIDATE_LEASE_MS` — Durable claim floor preventing overlapping alarms from repeating reconciliation; the effective lease covers configured liveness-probe, node-call, and minimum-alarm-delay budgets (default: 30000)
- `TASK_RECONCILIATION_PROBE_MAX_ATTEMPTS` — Consecutive inconclusive task reconciliation attempts before quarantine (default: 3)
- `TASK_RECONCILIATION_QUARANTINE_MS` — Cooldown after task reconciliation exhausts its inconclusive-attempt budget (default: 300000)
- `SESSION_ACTIVITY_PROBE_TIMEOUT_MS` — Bounded authoritative SessionHost inventory request timeout (default: 5000)
- `SESSION_ACTIVITY_PROBE_MAX_ATTEMPTS` — Unreachable probes before the stale activity mirror is quarantined without terminalization (default: 3)
- `SESSION_ACTIVITY_PROBE_MAX_CANDIDATES` — Maximum SessionHost inventory probes per ProjectData alarm pass (default: 10)
- `SESSION_TASK_REPAIR_BATCH_SIZE` — Maximum legacy taskless chat sessions repaired per 5-minute sweep (default: 25; capped at 200)
- `TERMINAL_SESSION_RECONCILE_PROJECT_BATCH_SIZE` — Maximum projects inspected for stale active ProjectData session ledgers per 5-minute sweep (default: 25; capped at 200)
- `TERMINAL_SESSION_RECONCILE_BATCH_SIZE` — Maximum active ProjectData `chat_sessions` candidates reconciled per project per 5-minute sweep (default: 25; capped at 200)
- `TERMINAL_SESSION_SUMMARY_RECONCILE_BATCH_SIZE` — Maximum active D1 `session_summaries` candidates reconciled globally per 5-minute sweep (default: 25; capped at 200)
- `TERMINAL_SESSION_RECONCILE_DEFER_MS` — Retry delay for live-head, snapshot-protected, or temporarily ineligible terminal-session ledger candidates (default: 3600000; capped at 86400000)
- `TASK_RUN_ABSOLUTE_CEILING_MS` — Absolute runaway-cost ceiling that fails even a demonstrably live task (default: 86400000 / 24h). Aged from the current runtime generation (`workspaces.created_at`), not `tasks.started_at`. Skipped when no runtime generation is allocated or the chat session holds a restorable sleep record; a sleep that is only in flight defers it for at most `TASK_RUN_ABSOLUTE_CEILING_SLEEP_GRACE_MS`
- `TASK_RUN_ABSOLUTE_CEILING_SLEEP_GRACE_MS` — Longest the absolute ceiling defers to a sleep still in flight (scheduled, capturing, stopping or retrying) once passed, measured on runtime-generation age so sleep retries cannot renew it; keep it above `SESSION_SLEEP_IN_FLIGHT_MAX_AGE_MS` (default: 3600000 / 1h)
- `SESSION_ACTIVITY_STALE_THRESHOLD_MS` — Threshold before stale working activity is checked against authoritative SessionHost inventory (default: 300000)
- `NODE_HEARTBEAT_STALE_SECONDS` — Staleness threshold for node health
- `TASK_LIVENESS_PROBE_TIMEOUT_MS` — Per-candidate timeout for ACP and Instant lifecycle probes used by ProjectData heartbeat deferral, idle cleanup, and stuck-task reconciliation; timeout is inconclusive (default: 5000)
- `TASK_LIVENESS_MAX_ACP_SESSIONS` — Maximum task-scoped ACP sessions inspected per liveness probe (default: 5)
- `TASK_LIVENESS_NODE_HEALTH_PROBE_TIMEOUT_MS` — Timeout for stale-VM-node health probes used by ProjectData idle cleanup and stuck-task reconciliation; a timeout is inconclusive and preserves the task/workspace (default: 5000)
- `NODE_PROVISIONING_REQUEST_TIMEOUT_MS` — Durable direct provisioning allocation/reconciliation request budget, also used for background readiness and workspace dispatch (default: 5000).
- `NODE_PROVISIONING_RETRY_INTERVAL_MS` — Delay between durable direct provisioning attempts (default: 30000).
- `NODE_PROVISIONING_MAX_AGE_MS` — Maximum age of a durable direct provisioning intent before retries stop (default: 900000 / 15 min).
- `NODE_PROVISIONING_MAX_ATTEMPTS` — Maximum durable direct provisioning attempts before retries stop (default: 30). Either allocation age or attempt exhaustion starts a separate bounded diagnostic publication phase using the same retry interval and attempt limit. The age limit applies only to allocation/reconciliation; the unresolved intent is retained. Empty provider inventory never authorizes another create or proves cleanup. All four `NODE_PROVISIONING_*` overrides are optional positive integers; invalid or unset values use their defaults.
- `NODE_AGENT_READY_TIMEOUT_MS` — Max wait for freshly provisioned node-agent health
- `NODE_AGENT_READY_POLL_INTERVAL_MS` — Polling interval for fresh-node readiness checks
- `VM_AGENT_REQUIRED_VERSION` — Deployment-generated required vm-agent build for reusable VM nodes. Official deploys set this from the last commit that changed a vm-agent **build input** (see `scripts/deploy/resolve-vm-agent-release.sh`), not from the deployment commit, so Worker-only deploys leave the existing node pool reusable. Build inputs are `packages/vm-agent/**` minus exactly four pathspecs: `.claude/`, the top-level `AGENTS.md`, `**/*_test.go` and `*_test.go`. A commit touching **only** those paths does not rotate the release and does not drain the node pool. Anything else under the directory does rotate it, including a file that looks like documentation — the list excludes rather than includes on purpose, so an unrecognised new file fails safe by rotating (wasteful) instead of silently serving a stale binary under a current version. If a new non-build file should stop rotating the release, add it to `AGENT_SOURCE_EXCLUDES` rather than assuming its extension exempts it. Unset disables rollout gating for local/manual or skip-agent deploys.
- `TASK_RUNNER_STEP_MAX_RETRIES` — Max retries per TaskRunner step before failing the task (default: 3)
- `TASK_RUNNER_RETRY_BASE_DELAY_MS` — Base delay for TaskRunner retry backoff (default: 5000)
- `TASK_RUNNER_RETRY_MAX_DELAY_MS` — Maximum delay for TaskRunner retry backoff (default: 60000)
- `TASK_RUNNER_AGENT_POLL_INTERVAL_MS` — TaskRunner D1 poll interval while waiting for a fresh provisioned VM agent (default: 5000)
- `TASK_RUNNER_AGENT_READY_TIMEOUT_MS` — TaskRunner max wait for a provisioned VM agent before failing the task (default: 900000)
- `TASK_RUNNER_AGENT_READY_FRESHNESS_SKEW_MS` — Timestamp skew tolerated between TaskRunner wait start, node heartbeat, and `/ready` signals (default: 30000)
- `TASK_RUNNER_WORKSPACE_DISPATCH_TIMEOUT_MS` — Max wait for VM-agent workspace dispatch acknowledgement (default: 600000)
- `TASK_RUNNER_WORKSPACE_DISPATCH_BASE_DELAY_MS` — Base delay for workspace dispatch retry backoff (default: 30000)
- `TASK_RUNNER_WORKSPACE_DISPATCH_MAX_DELAY_MS` — Maximum delay for workspace dispatch retry backoff (default: 120000)
- `TASK_RUNNER_WORKSPACE_READY_TIMEOUT_MS` — Max wait for workspace-ready callback (default: 1800000)
- `TASK_RUNNER_WORKSPACE_READY_POLL_INTERVAL_MS` — D1 poll interval during the TaskRunner workspace-ready step (default: 30000)
- `TASK_RUNNER_PROVISION_POLL_INTERVAL_MS` — TaskRunner provision status poll interval (default: 10000)
- `TASK_RUNNER_PROVISION_TIMEOUT_MS` — Max time for TaskRunner node provisioning before permanent failure (default: 900000)
- `HETZNER_API_TIMEOUT_MS` — Timeout for Hetzner Cloud API calls (default: 30000)
- `HETZNER_CAPACITY_RETRY_INITIAL_DELAY_MS` — Initial delay for transient Hetzner capacity retry backoff (default: 15000)
- `HETZNER_CAPACITY_RETRY_MAX_DELAY_MS` — Maximum delay per transient Hetzner capacity retry wait (default: 120000)
- `HETZNER_CAPACITY_RETRY_MAX_ATTEMPTS` — Maximum transient Hetzner capacity retry attempts (default: 10)
- `HETZNER_CAPACITY_RETRY_BUDGET_MS` — Total transient Hetzner capacity retry budget (default: 300000)
- `CF_API_TIMEOUT_MS` — Timeout for Cloudflare DNS API calls (default: 30000)
- `NODE_AGENT_REQUEST_TIMEOUT_MS` — Timeout for Node Agent HTTP requests (default: 30000)
### Workspace Resource History
- `WORKSPACE_RESOURCE_TOOL_NAME_MAX_BYTES` — Maximum UTF-8 bytes retained and returned for one resource-history tool name; truncation keeps complete Unicode characters (default: `256`).
### Audio/Transcription
- `WHISPER_MODEL_ID` — Workers AI model for transcription (default: `@cf/openai/whisper-large-v3-turbo`)
- `MAX_AUDIO_SIZE_BYTES` — Maximum audio upload size (default: 10485760)
- `MAX_AUDIO_DURATION_SECONDS` — Maximum recording duration (default: 60)
- `RATE_LIMIT_TRANSCRIBE` — Transcriptions allowed per user per window (default: 30)
- `RATE_LIMIT_TRANSCRIBE_WINDOW_SECONDS` — Transcription rate-limit window in seconds (default: 60)
### Session Summarization (Fork / Retry)
- `RATE_LIMIT_SESSION_SUMMARIZE` — Fork (`fork-prepare`) + Retry (`summarize`) summaries per user per window, one shared bucket (default: 30)
- `RATE_LIMIT_SESSION_SUMMARIZE_WINDOW_SECONDS` — Summarization rate-limit window in seconds (default: 3600)
### Client Error Reporting
- `RATE_LIMIT_CLIENT_ERRORS` — Rate limit per hour per IP (default: 200)
- `MAX_CLIENT_ERROR_BATCH_SIZE` — Max errors per request (default: 25)
- `MAX_CLIENT_ERROR_BODY_BYTES` — Max request body size (default: 65536)
- `MAX_VM_AGENT_ERROR_BODY_BYTES` — Max VM agent error request body (default: 32768)
- `MAX_VM_AGENT_ERROR_BATCH_SIZE` — Max VM agent errors per request (default: 10)
- `MAX_VM_AGENT_ERROR_SOURCE_LENGTH` — Max redacted VM error source length (default: 256)
- `OBSERVABILITY_ERROR_MESSAGE_MAX_LENGTH` — Max persisted observability error message length (default: 2048)
- `OBSERVABILITY_ERROR_STACK_MAX_LENGTH` — Max persisted observability stack length (default: 4096)
- `OBSERVABILITY_ERROR_USER_AGENT_MAX_LENGTH` — Max persisted observability user-agent length (default: 512)
- `VM_INCIDENT_R2_PREFIX` — Private R2 object-key prefix (default: `diagnostic-incidents`; generated deployments use the Pulumi output)
- `VM_INCIDENT_ARTIFACT_MAX_BYTES` — Max compressed artifact bytes (default: 2097152)
- `VM_INCIDENT_REGISTRATION_MAX_BYTES` — Max artifact registration body bytes (default: 262144)
- `VM_INCIDENT_MANIFEST_MAX_BYTES` — Max redacted manifest bytes (default: 131072)
- `VM_INCIDENT_PREVIEW_MAX_BYTES` — Max redacted preview bytes (default: 131072)
- `VM_INCIDENT_MAX_ARTIFACTS_PER_NODE` — Active artifact quota per node (default: 50)
- `VM_INCIDENT_MAX_BYTES_PER_NODE` — Active expected-byte quota per node (default: 104857600)
- `VM_INCIDENT_RETENTION_DAYS` — Private object and active metadata retention (default: 7; generated deployments use the Pulumi output)
- `VM_INCIDENT_METADATA_RETENTION_DAYS` — Expired metadata retention after object deletion (default: 30)
- `VM_INCIDENT_PENDING_TIMEOUT_MINUTES` — Incomplete upload timeout and upload-lease duration (default: 30)
- `VM_INCIDENT_RECONCILE_BATCH_SIZE` — Max rows repaired per scheduled pass (default: 50; minimum: 6)
### Project File Library
- `LIBRARY_LIST_DEFAULT_PAGE_SIZE` — Default file-list page size (default: 50)
- `LIBRARY_LIST_MAX_PAGE_SIZE` — Maximum file-list page size (default: 200)
- `LIBRARY_TAG_QUERY_BATCH_SIZE` — File IDs per tag metadata lookup query (default: 80, capped below D1 bind-variable limits)
- `LIBRARY_PROJECT_DELETE_CLEANUP_BATCH_SIZE` — Maximum project-owned R2 library objects listed and deleted per page after project deletion (default: `1000`, capped at R2's page maximum)
- Other library upload, directory, search, preview, and encryption settings are listed in `apps/api/.env.example`.
### Codex OAuth Refresh Proxy (`CodexRefreshLock` DO + `/api/auth/codex-refresh`)
- `CODEX_REFRESH_PROXY_ENABLED` — Kill switch; set to `'false'` to disable the proxy entirely (default: enabled)
- `CODEX_REFRESH_UPSTREAM_URL` — OpenAI OAuth token endpoint (default: `https://auth.openai.com/oauth/token`)
- `CODEX_REFRESH_UPSTREAM_TIMEOUT_MS` — Timeout for upstream fetch (default: 10000)
- `CODEX_REFRESH_LOCK_TIMEOUT_MS` — Max DO lock hold time per refresh (default: 30000)
- `CODEX_CLIENT_ID` — Public OAuth client_id for Codex (default: `app_EMoamEEZ73f0CkXaXp7hrann`)
- `CODEX_EXPECTED_SCOPES` — Comma-separated allowlist of scopes the upstream may return. **Unset uses the default allowlist** (`openid,profile,email,offline_access`). Set to empty string (`""`) to disable validation entirely (escape hatch for provider-driven scope additions). Unexpected scopes block the refresh with 502; the previous token remains valid. (MEDIUM #6 fix)
- `RATE_LIMIT_CODEX_REFRESH_PER_HOUR` — Per-user refresh request cap per window (default: 30). Enforced atomically via DO storage, not KV. (MEDIUM #5 fix)
- `RATE_LIMIT_CODEX_REFRESH_WINDOW_SECONDS` — Rate-limit window length in seconds (default: 3600)
### Credential Routes Rate Limits
- `RATE_LIMIT_CREDENTIAL_UPDATE` — Credential mutation cap used by user/project agent-key writes and the atomic `PUT /api/gcp/service-account` and superadmin Google infrastructure OAuth rotation paths.
### Generic Webhook Triggers
- `TRIGGER_STALE_EXECUTION_TIMEOUT_MS` — Age before running executions are checked against linked task liveness (default: `1800000`)
- `TRIGGER_STALE_QUEUED_TIMEOUT_MS` — Age before queued executions are checked against linked task liveness (default: `300000`)
- `TRIGGER_EXECUTION_HARD_MAX_RESIDENCE_HOURS` — Hard maximum execution residence backstop. The cleanup/admission/incident-dispatch paths still read linked task liveness so this backstop cannot free concurrency for a live task by itself (default: `48`)
- `TRIGGER_EXECUTION_LOG_RETENTION_DAYS` — Completed/failed/skipped execution log retention (default: `90`)
- `TRIGGER_EXECUTION_CLEANUP_ENABLED` — Trigger execution cleanup kill switch (default: enabled; set to `false` to disable)
- `TRIGGER_STALE_RECOVERY_BATCH_SIZE` — Maximum stale execution candidates processed per sweep (default: `100`)
- `WEBHOOK_TRIGGERS_ENABLED` — Public ingress kill switch (default: `true`)
- `WEBHOOK_CREDENTIAL_CLAIM_TTL_SECONDS` — Authenticated one-time MCP credential claim lifetime (default: `600`)
- `WEBHOOK_TRIGGER_MAX_BODY_BYTES` — Maximum JSON request body (default: `65536`)
- `WEBHOOK_TRIGGER_MAX_FILTERS` — Maximum deterministic filters per trigger (default: `10`)
- `WEBHOOK_TRIGGER_MAX_FILTER_PATH_LENGTH` — Maximum filter dot-path length (default: `200`)
- `WEBHOOK_TRIGGER_MAX_FILTER_PATH_DEPTH` — Maximum filter nesting depth at evaluation time (default: `8`)
- `WEBHOOK_TRIGGER_MAX_INCLUDED_HEADERS` — Maximum safe request headers copied into template context (default: `10`)
- `WEBHOOK_TRIGGER_MAX_HEADER_NAME_LENGTH` — Maximum configured included-header name length (default: `100`)
- `WEBHOOK_TRIGGER_MAX_SOURCE_LABEL_LENGTH` — Maximum optional source label length (default: `100`)
- `WEBHOOK_TRIGGER_MAX_IDEMPOTENCY_KEY_LENGTH` — Maximum `Idempotency-Key` length (default: `200`)
- `WEBHOOK_INGRESS_RATE_LIMIT_PER_MINUTE` — Best-effort pre-auth request damping per IP/window (default: `120`)
- `WEBHOOK_TRIGGER_RATE_LIMIT_PER_MINUTE` — Best-effort request damping per trigger/window (default: `60`)
- `WEBHOOK_INVALID_TOKEN_RATE_LIMIT_PER_MINUTE` — Best-effort invalid-token request damping per IP/window (default: `30`)
- `WEBHOOK_RATE_LIMIT_WINDOW_SECONDS` — Fixed rate-limit window length (default: `60`)
- `WEBHOOK_DELIVERY_RETENTION_DAYS` — Retention for redacted delivery audit metadata (default: `7`)
- `WEBHOOK_DELIVERY_CLEANUP_BATCH_SIZE` — Maximum expired audit rows deleted per cleanup pass (default: `500`)
- `WEBHOOK_DELIVERY_DEFAULT_PAGE_SIZE` — Default delivery-history page size (default: `25`)
- `WEBHOOK_DELIVERY_MAX_PAGE_SIZE` — Maximum delivery-history page size (default: `100`)
- `WEBHOOK_DELIVERY_PROCESSING_LEASE_SECONDS` — Recovery lease for processing deliveries without a submitted task (default: `300`)
- `MAX_TRIGGERS_PER_PROJECT` — Platform default trigger cap per project (default: `20`). Editable per project in the Settings → Scaling & Scheduling → Task Limits field; a `NULL` `projects.max_triggers` falls back to this env default.
### Trial Onboarding (`/try` flow)
Trial configuration is currently sourced from `apps/api/.env.example` and `apps/api/src/env.ts`. Summary:
- `TRIAL_CLAIM_TOKEN_SECRET` — Worker secret; HMAC key for trial cookies (auto-provisioned by Pulumi)
- `TRIAL_MONTHLY_CAP`, `TRIAL_WORKSPACE_TTL_MS`, `TRIAL_DATA_RETENTION_HOURS` — Global cap + lifetimes
- `TRIAL_ANONYMOUS_USER_ID`, `TRIAL_ANONYMOUS_INSTALLATION_ID` — Sentinel rows for pre-claim ownership
- `TRIAL_AGENT_TYPE_STAGING`, `TRIAL_AGENT_TYPE_PRODUCTION`, `TRIAL_DEFAULT_WORKSPACE_PROFILE` — Agent + profile selection
- `TRIALS_ENABLED_KV_KEY`, `TRIAL_KILL_SWITCH_CACHE_MS` — Kill switch
- `TRIAL_EXPIRE_BATCH_SIZE`, `TRIAL_CLEANUP_BATCH_SIZE`, `TRIAL_CLEANUP_DEADLINE_MS`, `TRIAL_NODE_DELETION_LOCK_STALE_MS` — Expired-trial cron cleanup bounds and stale deletion-lock retry
- `TRIAL_ORCHESTRATOR_OVERALL_TIMEOUT_MS`, `TRIAL_ORCHESTRATOR_STEP_MAX_RETRIES`, `TRIAL_ORCHESTRATOR_RETRY_BASE_DELAY_MS`, `TRIAL_ORCHESTRATOR_RETRY_MAX_DELAY_MS` — Orchestrator retry budget
- `TRIAL_ORCHESTRATOR_NODE_READY_TIMEOUT_MS`, `TRIAL_ORCHESTRATOR_AGENT_READY_TIMEOUT_MS`, `TRIAL_ORCHESTRATOR_WORKSPACE_READY_TIMEOUT_MS`, `TRIAL_ORCHESTRATOR_WORKSPACE_READY_POLL_INTERVAL_MS` — Step-level timeouts
- `TRIAL_VM_SIZE`, `TRIAL_VM_LOCATION` — VM overrides for trial workspaces
- `TRIAL_GITHUB_TIMEOUT_MS` — Per-request timeout for the default-branch probe (`fetchDefaultBranch`); falls back to `main` on timeout/404/error
- `TRIAL_KNOWLEDGE_GITHUB_TIMEOUT_MS`, `TRIAL_KNOWLEDGE_MAX_EVENTS` — Fast-path knowledge probe tunables
## VM Agent Environment Variables
### Container/User
- `CONTAINER_USER` — Optional `docker exec -u` override; when unset, auto-detects effective devcontainer user
### Workspace Builds
- `WORKSPACE_BUILD_QUEUE_DEPTH` — Concurrent devcontainer build slots per workspace VM (default: `1`, supported range: `1` through `16`). Generated cloud-init passes this from the Worker env to newly provisioned VM Agent systemd services. Invalid values do not enable additional build slots and fall back to the default one-slot behavior.
### Git Operations
- `GIT_CREDENTIAL_TIMEOUT` — Go duration for credential-helper callbacks to the local VM agent, such as `5s` or `1750ms` (default: `5s`)
- `GIT_EXEC_TIMEOUT` — Timeout for git commands via docker exec (default: 30s)
- `GIT_WORKTREE_TIMEOUT` — Timeout for git worktree create/remove (default: 30s)
- `WORKTREE_CACHE_TTL` — Cache duration for parsed `git worktree list` results (default: 5s)
- `MAX_WORKTREES_PER_WORKSPACE` — Max worktrees allowed per workspace (default: 5)
- `GIT_FILE_MAX_SIZE` — Max file size for git/file endpoint (default: 1048576)
### Session Snapshots
Generated deployments validate and pass these values through cloud-init to newly provisioned VM Agent systemd services. Instant containers receive the same values at launch.
- `SESSION_SNAPSHOT_OPERATION_TIMEOUT` — Deadline for one asynchronous checkpoint operation (default: `15m`)
- `SESSION_SNAPSHOT_PROGRESS_REPORT_INTERVAL` — Minimum interval between progress callbacks while a checkpoint continues making progress (default: `15s`)
- `SESSION_SNAPSHOT_PROGRESS_REPORT_TIMEOUT` — Timeout for each best-effort progress callback to the control plane (default: `5s`)
### Workspace Callback Token Renewal
The agent renews each workspace callback token after a successful node heartbeat once the token is past the refresh ratio (`internal/server/workspace_callback_token_renewal.go`). These use their defaults unless set in the agent service environment.
- `WORKSPACE_CALLBACK_TOKEN_REFRESH_RATIO` — Fraction of a workspace token's lifetime after which the agent renews it (default: `0.5`, clamped to `0.1`–`0.9`; the control plane's `CALLBACK_TOKEN_REFRESH_THRESHOLD_RATIO` still decides)
- `WORKSPACE_CALLBACK_TOKEN_RENEWAL_TIMEOUT` — Timeout for one renewal request (default: `15s`)
- `WORKSPACE_CALLBACK_TOKEN_RENEWAL_RETRY_INITIAL` — First backoff after a transient renewal failure (default: `1m`)
- `WORKSPACE_CALLBACK_TOKEN_RENEWAL_RETRY_MAX` — Backoff ceiling, also the wait after a "not yet due" answer (default: `30m`)
- `MSG_AUTH_RENEWAL_WAIT` — How long chat-message delivery may stay paused on a rejected (401) workspace token before the pause is reported as an error (default: `15m`). Held messages are kept either way.
### File Operations
- `FILE_LIST_TIMEOUT` — Timeout for file listing commands (default: 10s)
- `FILE_LIST_MAX_ENTRIES` — Max entries per directory listing (default: 1000)
- `FILE_FIND_TIMEOUT` — Timeout for recursive file index (default: 15s)
- `FILE_FIND_MAX_ENTRIES` — Max entries returned by file index (default: 5000)
### Error Reporting
Generated deployments validate and pass these values through cloud-init to newly provisioned VM Agent systemd services.
- `ERROR_REPORT_FLUSH_INTERVAL` — Background error flush interval (default: 30s)
- `ERROR_REPORT_MAX_BATCH_SIZE` — Immediate flush threshold (default: 10)
- `ERROR_REPORT_MAX_BATCH_BYTES` — Max serialized batch bytes, aligned with Worker ingestion (default: 32768)
- `ERROR_REPORT_MAX_QUEUE_SIZE` — Max durable SQLite outbox entries (default: 1000)
- `ERROR_REPORT_HTTP_TIMEOUT` — HTTP POST timeout for error reports (default: 10s)
- `ERROR_REPORT_RETRY_INITIAL` / `ERROR_REPORT_RETRY_MAX` — Exponential retry bounds (defaults: 1s / 5m)
- `ERROR_REPORT_MAX_ATTEMPTS` — Retry attempts before a report expires locally (default: 20)
- `ERROR_REPORT_DB_PATH` — Durable SQLite outbox path (default: next to the VM Agent persistence database)
- `ERROR_REPORT_DB_BUSY_TIMEOUT` — SQLite outbox contention timeout (default: 5s)
- `ERROR_REPORT_SPOOL_DIR` — Private evidence spool path (default: `diagnostic-incidents` beside the persistence database)
- `ERROR_REPORT_ARTIFACT_MAX_BYTES` — Max compressed artifact bytes (default: 2097152)
- `ERROR_REPORT_SPOOL_MAX_BYTES` — Max local evidence spool bytes (default: 20971520)
- `ERROR_REPORT_RETENTION` — Max local report/evidence retention (default: 24h)
- `ERROR_REPORT_COLLECTOR_TIMEOUT` — Shared allowlisted collector deadline (default: 10s)
- `ERROR_REPORT_MAX_COLLECTOR_DOCS` — Max structured collector documents (default: 8)
- `ERROR_REPORT_MAX_DOCUMENT_BYTES` — Cumulative redacted preview budget (default: 131072)
- `ERROR_REPORT_MAX_VALUE_DEPTH` / `ERROR_REPORT_MAX_VALUE_ITEMS` — Recursive sanitizer bounds (defaults: 8 / 256)
- `ERROR_REPORT_MAX_STRING_BYTES` — Per-string sanitizer limit (default: 4096)
- `ERROR_REPORT_EVENT_LIMIT` — Max structured event or workspace previews collected (default: 100)
- `ERROR_REPORT_RESPONSE_MAX_BYTES` — Max control-plane response bytes read into a diagnostic error (default: 4096)
- `ERROR_REPORT_STORED_ERROR_MAX_BYTES` — Max bytes persisted for a durable reporter error (default: 512)
- `ERROR_REPORT_COLLECTOR_CONCURRENCY` — Max automatic-evidence collectors running concurrently (default: 1)
### Message Reporting
- `MSG_MAX_MESSAGE_CONTENT_BYTES` — Max content bytes of one message; longer content keeps its longest rune-aligned prefix plus a `[truncated]` marker (default: 102400, matches the API's `MESSAGE_SIZE_THRESHOLD`)
- `MSG_BATCH_MAX_BYTES` — Max serialized request body per batch (default: 262144, matches the API's `MAX_MESSAGES_PAYLOAD_BYTES`). Every message is shaped at enqueue to fit one request on its own: tool metadata that cannot fit is replaced by an identity summary flagged `contentTruncated`/`transportTruncated` with `originalSizeBytes`. Keep both values at or below the API's limits.
### ACP (Agent Communication Protocol)
- `CODEX_RUNTIME_INSTALL_KILL_GRACE` — Additional grace after the container install deadline before forced termination (default: 5s; nonpositive values use the default).
- `CODEX_RUNTIME_INSTALL_TIMEOUT` — VM-agent host setting bounding opted-in pinned Codex download/install (default: 5m; nonpositive values use the default). Enforced inside the devcontainer as well as on the host. Instant uses a preinstalled image and never installs as the runtime user.
- `ACP_MESSAGE_BUFFER_SIZE` — Max buffered messages per SessionHost for late-join replay (default: 5000)
- `ACP_VIEWER_SEND_BUFFER` — Per-viewer send channel buffer size (default: 256)
- `ACP_PING_INTERVAL` — WebSocket ping interval for stale connection detection (default: 30s)
- `ACP_PONG_TIMEOUT` — WebSocket pong deadline after ping (default: 10s)
- `ACP_PROMPT_TIMEOUT` — Max ACP prompt runtime for workspace sessions; 0 = no timeout (default: 0)
- `ACP_TASK_PROMPT_TIMEOUT` — Max ACP prompt runtime for task-driven sessions (default: 8h)
- `ACP_PROMPT_CANCEL_GRACE_PERIOD` — Grace wait for the cancelled prompt to settle before it is finished as `cancelled` and the agent is restarted (default: 5s). Bound to the cancelled prompt; never affects a later prompt
- `ACP_PROMPT_RETRY_MAX_RETRIES` — Max transient provider prompt retries after the initial attempt (default: 2)
- `ACP_PROMPT_RETRY_INITIAL_BACKOFF` — Initial backoff before retrying transient provider prompt errors (default: 15s)
- `ACP_PROMPT_RETRY_MAX_BACKOFF` — Max exponential backoff for transient provider prompt retries (default: 2m)
- `ACTIVITY_REREPORT_INTERVAL` — Re-send `prompting` activity while a prompt is active (default: 60s)
- `ACP_HARNESS_ACTIVITY_REPORT_DEBOUNCE` — Debounce window for coalescing high-frequency ACP harness/tool-call activity reports before POSTing to the control plane (default: 750ms)
- `ACP_CHECKPOINT_PREEMPT_GRACE` — Graceful ACP cancel/close wait before harness force-stop (default: 30s)
- `ACP_CHECKPOINT_PREEMPT_MAX_GRACE` — Maximum caller-selected checkpoint rollover grace (default: 2m)
- `ACP_CHECKPOINT_ROLLOVER_TIMEOUT` — Full checkpoint restart and strict LoadSession deadline (default: 2m)
- `ACTIVITY_TERMINAL_REPORT_ATTEMPTS` — Retry attempts for terminal activity reports (`idle`, `recovering`, `error`) (default: 5)
- `ACTIVITY_TERMINAL_REPORT_BACKOFF` — Backoff between terminal activity report retries (default: 1s)
- `ACP_IDLE_SUSPEND_TIMEOUT` — Idle timeout before auto-suspending agent session (default: 30m)
- `ACP_NOTIF_SERIALIZE_TIMEOUT` — Max wait for previous session/update processing before delivering next (default: 5s)
### Events
- `MAX_NODE_EVENTS` — Max node-level events retained in memory (default: 500)
- `MAX_WORKSPACE_EVENTS` — Max workspace-level events retained in memory (default: 500)
### System Info
- `SYSINFO_DOCKER_TIMEOUT` — Timeout for Docker CLI commands during system info collection (default: 10s)
- `SYSINFO_VERSION_TIMEOUT` — Timeout for version-check commands (default: 5s)
- `SYSINFO_CACHE_TTL` — Cache duration for system info results (default: 5s)
### Active Resource Monitoring
- `DEFAULT_RESOURCE_EVENT_BUFFER_SIZE` — Capacity of each bounded pressure/Docker event queue (default: 64, positive integer)
- `DEFAULT_PSI_POLL_INTERVAL_SECONDS` — PSI memory pressure polling interval, in seconds (default: 10)
- `DEFAULT_CONTAINER_STATS_INTERVAL_SECONDS` — Per-container `docker stats` polling interval, in seconds (default: 30)
- `DEFAULT_PSI_MEMORY_SOME_WARNING_THRESHOLD` — Warning threshold for memory `some` PSI `avg10`/`avg60` (default: 25.0)
- `DEFAULT_PSI_MEMORY_SOME_CRITICAL_THRESHOLD` — Critical threshold for memory `some` PSI `avg10`/`avg60` (default: 50.0)
- `DEFAULT_PSI_MEMORY_FULL_WARNING_THRESHOLD` — Warning threshold for memory `full` PSI `avg10`/`avg60` (default: 10.0)
- `DEFAULT_PSI_MEMORY_FULL_CRITICAL_THRESHOLD` — Critical threshold for memory `full` PSI `avg10`/`avg60` (default: 25.0)
- `DEFAULT_EVICTION_DEBOUNCE_SECONDS` — Minimum cooldown between ResourceGuard eviction attempts, in seconds (default: 30)
- `DEFAULT_EVICTION_SNAPSHOT_TIMEOUT_SECONDS` — Deadline for pre-stop eviction snapshot capture, in seconds (default: 120)
- `DEFAULT_EVICTION_DOCKER_STOP_TIMEOUT_SECONDS` — Grace period passed to `docker stop --time` during eviction, in seconds (default: 10)
- `DEFAULT_EVICTION_CALLBACK_RETRY_MAX_SECONDS` — Durable callback backoff cap, in seconds (default: 300). Retry eligibility also respects the complete operation lease (default: 60 seconds), which can exceed the cap; delivery is heartbeat-paced
- `DEFAULT_EVICTION_RESOLVE_TIMEOUT_SECONDS` — Deadline for resolving a pressured Docker container to a workspace before eviction, in seconds (default: 5)
### Repeated agent check-ins
`TASK_RECONCILIATION_MAX_CHECKINS` (default `3`) caps automatic check-ins per durable no-progress episode. Human input or a newly completed tool call resets the budget; assistant error/text and system messages do not. A recognized unsupported-model runtime error pauses check-ins before another delivery. At the cap, SAM pauses nudges and asks Clef once using the existing `STALLED_TASK_CLASSIFIER_*` model, timeout, confidence, and transcript limits (without the long-turn age gate). Disabled/unavailable/uncertain classification never grants additional retries. The session/work remains intact; the attention notice explains how to retry.