Skip to content

Configuration Reference

SAM uses environment variables for platform configuration. User-specific settings (cloud provider tokens, agent API keys) are stored encrypted in the database, not as environment variables.

These are Cloudflare Worker secrets, set during deployment. Pulumi auto-generates security keys on first deploy.

SecretDescription
ENCRYPTION_KEYAES-256-GCM master key. Used for BetterAuth session cookies and user credential encryption unless a purpose-specific override below is set (auto-generated)
BETTER_AUTH_SECRETOptional purpose-specific override for BetterAuth session cookie signing/encryption. Falls back to ENCRYPTION_KEY when unset (apps/api/src/lib/secrets.ts)
CREDENTIAL_ENCRYPTION_KEYOptional purpose-specific override for AES-GCM encryption of user cloud/agent credentials. Falls back to ENCRYPTION_KEY when unset (apps/api/src/lib/secrets.ts)
JWT_PRIVATE_KEYRSA-2048 private key for signing tokens (auto-generated)
JWT_PUBLIC_KEYRSA-2048 public key for token verification (exposed via JWKS)
DEPLOY_SIGNING_PRIVATE_KEYEd25519 private key for signing deployment apply payloads (auto-generated)
DEPLOY_SIGNING_PUBLIC_KEYEd25519 public key derived during deployment for deployment node verification (auto-generated)
VAPID_PRIVATE_KEYBase64url P-256 private scalar used to authenticate Web Push delivery (auto-generated)
VAPID_PUBLIC_KEYUncompressed base64url P-256 public key returned to browsers at runtime (derived during deployment)
VAPID_SUBJECTRFC 8292 contact URI for Web Push, defaulting to the deployment app origin (generated during deployment)
CF_API_TOKENCloudflare API token for infrastructure, DNS, Origin CA certificate issuance, observability, AI Gateway, Containers, and admin logs. Requires Account → Containers → Edit and Account → SSL and Certificates → Edit.
CF_AIG_TOKENOptional narrower Cloudflare AI Gateway Unified Billing token
CF_ZONE_IDCloudflare zone ID for DNS record management
CF_ACCOUNT_IDCloudflare account ID
DEVCONTAINER_CACHE_CLOUDFLARE_API_TOKENOptional narrower Cloudflare token for managed devcontainer registry credentials
DEVCONTAINER_CACHE_CLOUDFLARE_ACCOUNT_IDOptional Cloudflare account override for managed devcontainer registry credentials
GITHUB_CLIENT_IDOptional fallback GitHub App client ID for OAuth; runtime admin config takes precedence
GITHUB_CLIENT_SECRETOptional fallback GitHub App client secret for OAuth; runtime admin config takes precedence
GITHUB_APP_IDOptional fallback GitHub App ID for installation tokens; runtime admin config takes precedence
GITHUB_APP_PRIVATE_KEYOptional fallback GitHub App private key (PEM or base64); runtime admin config takes precedence
GITHUB_APP_SLUGOptional fallback GitHub App URL slug; runtime admin config takes precedence
GITHUB_WEBHOOK_SECRETOptional fallback GitHub App webhook HMAC secret; runtime admin config takes precedence
GITLAB_HOSTOptional fallback GitLab OAuth host, such as https://gitlab.com; runtime admin config takes precedence
GITLAB_CLIENT_IDOptional fallback GitLab OAuth application ID; runtime admin config takes precedence
GITLAB_CLIENT_SECRETOptional fallback GitLab OAuth secret; runtime admin config takes precedence
TRIAL_CLAIM_TOKEN_SECRETTrial onboarding HMAC secret (auto-generated)

Set as [vars] in wrangler.toml or as environment variables:

VariableDefaultDescription
BASE_DOMAINRoot domain for the deployment (e.g., example.com)
PREVIEW_BASE_DOMAINpreview.BASE_DOMAINFull isolated hostname used for interactive HTML previews
PREVIEW_URL_TTL_SECONDS300Lifetime of project/file/version-scoped interactive preview URLs in seconds
PREVIEW_SIGNING_KEYgeneratedDeployment-owned HMAC key generated and persisted by Pulumi; not a manual prerequisite
VERSIONDeployment version string
SETUP_TOKENPlaintext first-run setup token generated during deploy and readable in the Cloudflare dashboard while setup is incomplete
SETUP_FORCE(unset)Set to true to reopen /setup for lockout recovery
SETUP_RATE_LIMIT_MAX_ATTEMPTS10Max setup-token attempts per identifier/window
SETUP_RATE_LIMIT_WINDOW_SECONDS900Setup-token attempt window in seconds
PLATFORM_CONFIG_CACHE_MS60000Per-isolate cache TTL for the resolved platform integration config (the GITHUB_*/GITLAB_*/GOOGLE_LOGIN_* fallbacks above and their runtime admin overrides). Resolving costs 13 D1 queries and runs on the auth preamble of every authenticated request. After a config change, isolates that already hold a cached copy converge within this window. Set to 0 to disable caching and always re-read D1.

Set in GitHub Settings → Environments → production:

VariableDescriptionExample
BASE_DOMAINDeployment domainexample.com
RESOURCE_PREFIXDomain-derived Cloudflare resource name prefixsa379a6
PULUMI_STATE_BUCKETR2 bucket for Pulumi statesa379a6-pulumi-state
CF_CONTAINER_ENABLEDOptional instant-session runtime toggle. Generated deploys default to true; set false to force VM runtime.false
D1_RESTORE_RECOVERY_WINDOW_DAYSOptional D1 restore window for accounts with narrower retention. Defaults to 30; range 130.7
D1_MIGRATION_CHURNING_TABLESOptional comma-separated <binding>.<table> subset of the reviewed retention/expiry table list. May narrow the built-in list but cannot expand it.OBSERVABILITY_DATABASE.platform_errors
D1_MIGRATION_CHURNING_TABLE_MAX_DECREASE_PERCENTMaximum allowed decrease for reviewed churning tables. Defaults to 50; range 0100. A decrease exactly at the limit is accepted.25

The reviewed default churning selectors are 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. All other application tables retain zero row-decrease tolerance. Leave D1_MIGRATION_CHURNING_TABLES unset to use the complete reviewed default list.

RESOURCE_PREFIX is generated from BASE_DOMAIN as s plus the first six hex characters of the domain’s SHA-256 hash. The self-host onboarding flow fills it in for you.

Required GitHub Actions secrets include CF_API_TOKEN, CF_ACCOUNT_ID, CF_ZONE_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, and PULUMI_CONFIG_PASSPHRASE. GitHub App/OAuth secrets (GH_CLIENT_ID, GH_CLIENT_SECRET, GH_APP_ID, GH_APP_PRIVATE_KEY, GH_APP_SLUG, GH_WEBHOOK_SECRET) and Google login OAuth secrets (GOOGLE_LOGIN_CLIENT_ID, GOOGLE_LOGIN_CLIENT_SECRET) are optional environment fallbacks; fresh deployments can set them through /setup instead. The separate Google infra/GCP OAuth pair (GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET) is used only for WIF and can be configured by a superadmin at /admin/integrations; runtime values override the environment fallback. Service-account JSON users need no infrastructure OAuth client. Deploy signing keys are generated and persisted by Pulumi during deployment; GitHub Environment values are only needed for explicit key overrides.

These variables affect the local sam CLI process only. They are not Worker runtime variables or GitHub Actions secrets.

VariableDefaultDescription
SAM_CLI_MAX_API_RESPONSE_BYTES1048576Maximum API response body bytes the CLI reads before truncating/aborting.

Codex and Claude Code guided subscription login have no feature-on environment variable. They are available by default when the deployment includes the SANDBOX, CREDENTIAL_SETUP_SESSION, and SETUP_SESSION_POOL Worker bindings generated by SAM’s deployment configuration. Omitting one of those bindings disables the guided flow. SANDBOX_ENABLED continues to control separate administrative Sandbox runtime surfaces and is not required for guided login.

VariableDefaultDescription
MAX_CONCURRENT_SETUP_SESSIONS2Maximum concurrent guided credential-setup sessions.
SETUP_SESSION_TTL_MS900000Guided session lifetime before automatic teardown.
SETUP_SESSION_CAPTURE_POLL_MS3000Interval for checking device-login and credential-capture state.
CODEX_DEVICE_AUTH_REQUEST_TIMEOUT_MS30000Timeout for each Codex app-server JSON-RPC request.
CLAUDE_SETUP_ENTER_DELAY_MS1000Delay before sending Enter as a separate stdin write after pasting Claude’s browser-displayed code.
CLAUDE_SETUP_EXCHANGE_TIMEOUT_MS120000Maximum wait for Claude’s CLI code exchange before a visible timeout.
CLAUDE_SETUP_REJECTION_SETTLE_MS400Wait for Claude CLI Ink redraws to settle before classifying an OAuth error.
CLAUDE_SETUP_VERIFICATION_POLL_MS500Interval for checking the sandbox handoff file for Claude’s browser-displayed code.
CLAUDE_SETUP_TTY_COLUMNS512PTY width for claude setup-token, reducing opaque-token wrapping.
CLAUDE_SETUP_OUTPUT_BUFFER_BYTES32768Maximum in-memory Claude PTY output retained for parsing.
CLAUDE_VERIFICATION_CODE_MAX_LENGTH1024Maximum accepted length of Claude’s browser-displayed code#state value.
CLAUDE_SETUP_ERROR_DETAIL_MAX_LENGTH160Maximum sanitized Claude CLI diagnostic length shown to the user.
CLAUDE_OAUTH_TOKEN_MAX_LENGTH8192Maximum captured Claude OAuth token length.
SETUP_SESSION_SWEEP_MAX_CANDIDATES50Maximum expired sessions cleaned up by one scheduled sweep.
POOL_LEASE_BUFFER_MS300000Grace period after session TTL before a leaked capacity lease self-prunes.

The variables below tune the Instant (Cloudflare Container) runtime — how long a session stays awake, how long a wake may take, and how many snapshot restores are attempted before a session is failed. See Instant Sessions for what each of these means to a user.

VariableDefaultDescription
CF_CONTAINER_ENABLEDtrueEnables Cloudflare Container instant sessions for matching profiles and zero-config runtime selection. Set false to force cloud VM runtime.
CF_CONTAINER_SLEEP_AFTER1hNormal inactivity window before an Instant container sleeps. Sleep remains recoverable through the runtime-neutral session snapshot.
CF_CONTAINER_ACTIVE_WORK_MAX_MS7200000Defensive maximum lifetime for an active-work keepalive lease.
CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS300000Interval used to renew the container activity timeout while prompt work is active.
CF_CONTAINER_WAKE_TIMEOUT_MS120000Maximum time for a sleeping container to launch, restore its snapshot, and accept the triggering request.
CF_CONTAINER_RECOVERY_MAX_ATTEMPTS2Maximum snapshot restore attempts before SAM reconciles the runtime, workspace, agent session, and active task to a visible terminal recovery failure.
INSTANT_STALE_CALLBACK_MARGIN_MS60000 (60 sec)Freshness margin used to reject destructive (error/failed) callbacks arriving from a superseded Instant container generation after the runtime row was reconciled by a completed recovery.
CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS120000Budget for the synchronous instant-session create-workspace request, which includes the repository clone inside the container.
CF_CONTAINER_CLONE_FILTERblob:noneGit partial-clone filter forwarded to instant containers as STANDALONE_CLONE_FILTER. Set off to force full clones.

Sleeping and reclaimed Instant and VM sessions are restored from a snapshot of the agent’s home directory and the repository work in progress. A complete snapshot is required before SAM tears down VM compute. None of these limits are surfaced in the UI, so operators should set expectations deliberately — see What gets restored.

VariableDefaultDescription
SESSION_SNAPSHOT_TTL_DAYS7Snapshot retention. A session sleeping longer than this cannot be fully restored.
SESSION_SNAPSHOT_TOTAL_BUDGET_BYTES268435456 (256 MiB)Max combined size of the home + work-in-progress snapshot. The higher default favors bounded retained R2 state over keeping a VM alive when a typical agent harness has accumulated substantial durable state.
SESSION_SNAPSHOT_ENTRY_THRESHOLD_BYTES268435456 (256 MiB)Largest single file the snapshot scanner will include. This matches the total budget so durable agent state databases are not skipped solely because they are larger than the former 50 MiB cap.
SESSION_SNAPSHOT_TRANSFER_IDLE_TIMEOUT_MS30000 (30 sec)No-progress timeout for each snapshot upload or download.
SESSION_SNAPSHOT_UPLOAD_URL_TTL_SECONDS900 (15 min)Lifetime of direct R2 upload URLs used so large snapshots do not traverse the Worker request-body boundary. Current agents bind exact length and SHA-256; busy legacy VM agents stream through a current same-user VM relay that independently authenticates both nodes and removes callback credentials before R2. When R2 S3 credentials are unavailable, SAM retains the Worker upload path.
SESSION_SNAPSHOT_REQUEST_TIMEOUT_MS300000 (5 min)Budget for the vm-agent to accept the final checkpoint request. Durable completion is governed by progress reporting rather than this fixed wall clock.
SESSION_SNAPSHOT_PROGRESS_IDLE_TIMEOUT_MS120000 (2 min)No-progress watchdog for an accepted final checkpoint. Current vm-agents periodically advance D1 progress while walking HOME or uploading artifacts; if progress stops, SAM completes a degraded snapshot so idle compute can still be released visibly.
SESSION_SNAPSHOT_POLL_INTERVAL_MS1000 (1 sec)Interval used while the Worker waits for a VM agent’s asynchronous final checkpoint to commit in D1.
SESSION_SNAPSHOT_OPERATION_TIMEOUT15mVM-agent process deadline for one asynchronous checkpoint operation. This uses Go duration syntax.
SESSION_SNAPSHOT_PROGRESS_REPORT_INTERVAL15sVM-agent throttle for best-effort progress callbacks during data-scaled snapshot work. This uses Go duration syntax and is passed to newly provisioned VMs and Instant containers.
SESSION_SNAPSHOT_PROGRESS_REPORT_TIMEOUT5sVM-agent timeout for each best-effort snapshot progress callback. This uses Go duration syntax and is passed to newly provisioned VMs and Instant containers.
SESSION_SNAPSHOT_JSON_BODY_MAX_BYTES262144 (256 KB)Maximum snapshot coordination request size accepted by the Worker.
SESSION_SNAPSHOT_R2_PREFIXsession-snapshotsPrivate object prefix. Session objects are deleted by the Worker from D1 lifecycle state, not by object age.
SESSION_SNAPSHOT_RECOVERY_MAX_ATTEMPTS3Maximum replacement-VM wake attempts before the sleeping session becomes unavailable.
SESSION_SLEEP_AFTER_MS900000 (15 min)ProjectData-recorded idle interval before SAM automatically sleeps a VM session. Runtime heartbeats do not extend this clock. Completed tasks queue sleep immediately; a terminal prompt still marked active becomes eligible once this interval has elapsed.
SESSION_SLEEP_SWEEP_BATCH_SIZE10Maximum due session sleep candidates selected and individually claimed by one scheduled sweep.
SESSION_SLEEP_SWEEP_WALL_BUDGET_MS20000 (20 sec)Soft wall-clock budget for bounded D1/ProjectData eligibility and claim work. After a durable claim, final snapshot and teardown run through the scheduled event’s out-of-band lifetime. Remaining unclaimed rows stay due for the next sweep.
SESSION_SLEEP_RETRY_DELAY_MS300000 (5 min)Retry delay after a fail-closed automatic sleep attempt.
SESSION_SLEEP_MAX_ATTEMPTS9Automatic sleep attempts before SAM preserves compute and records an operator-visible failure. Raising the configured budget re-arms previously exhausted rows that are still below the new limit.
SESSION_SLEEP_CLAIM_LEASE_MS600000 (10 min)Time after which an interrupted automatic-sleep claim can be safely reclaimed.
HARNESS_BACKGROUND_WORK_LEASE_MS300000 (5 min)Finite sleep-protection lease renewed by normalized harness background-work lifecycle signals. Expiry fails open to ordinary idle-sleep eligibility so a missing terminal signal cannot pin compute forever.
HARNESS_BACKGROUND_WORK_MAX_DURATION_MS1800000 (30 min)Absolute ceiling, measured from the last harness lifecycle progress edge rather than the last heartbeat, on how long background work may defer sleep. The sliding lease above is refreshed by periodic re-reports, so an adapter faithfully re-reporting a stale task set (for example an abandoned run_in_background dev server) would otherwise pin compute awake indefinitely.
SESSION_SNAPSHOT_RECOVERY_CLAIM_LEASE_MS600000 (10 min)Time after which an interrupted replacement-runtime wake claim can be reconciled or reclaimed.
SESSION_LIFECYCLE_ERROR_MAX_LENGTH2048Maximum sleep/recovery diagnostic detail stored in lifecycle records.
SESSION_SNAPSHOT_PURGE_ENABLEDtrueEnables bounded expiry cleanup: terminalizes the sleeping chat, deletes its R2 objects, then removes D1 metadata.
SESSION_SNAPSHOT_PURGE_BATCH_SIZE250Maximum expired snapshot rows deleted per daily purge.
REQUIRE_APPROVAL(unset)Default signup approval gate. Superadmins can override it at runtime in Admin → Users without redeploying; when no runtime override exists, this value is used. The first genuine human becomes superadmin regardless of this flag — see First Login & Admin Access.
TRIAL_ANONYMOUS_USER_IDsystem_anonymous_trialsId of the internal anonymous-trial sentinel user, excluded from first-user superadmin checks. Override only if your deployment uses a different sentinel id.
CAPACITY_SIZE_FALLBACK_ENABLEDtrueWhen a new node’s VM size is exhausted on transient capacity, descend the size chain (large→medium→small). Only applies to default-derived sizes (project/platform default), never user-requested sizes. Set false to disable.
ORIGIN_CA_CERT_VALIDITY_DAYS7Validity for per-node Cloudflare Origin CA certificates issued from node-generated CSRs. Must be one of Cloudflare’s supported values: 7, 30, 90, 365, 730, 1095, or 5475.
VariableDefaultDescription
LIBRARY_PROJECT_DELETE_CLEANUP_BATCH_SIZE1000Maximum project-owned library objects listed and deleted per R2 page after project deletion. Values above R2’s 1,000-object page maximum are capped.

Deployment release and compose artifact retention

Section titled “Deployment release and compose artifact retention”

The scheduled Worker first reconciles provably stale non-terminal compose releases, then prunes terminal deployment releases outside the protected window (apps/api/src/scheduled/d1-retention.ts:runDeploymentReleaseRetention()). Terminal retention always retains the newest releases per environment and the version reported in deployment_environments.observed_applied_seq. The stale reconciler only marks a created/applying compose-artifact release failed when D1 shows old release status activity, stable authenticated deployment-node observed state, no recent release fetch/apply events, a valid manifest, and a release version that is not the observed applied version. Unknown statuses, malformed manifests, missing observed state, active applying observations, and recent release events fail closed. Compose artifact cleanup then re-derives references from the remaining manifests (apps/api/src/scheduled/compose-image-artifact-cleanup.ts:runComposeImageArtifactCleanup()).

VariableDefaultDescription
DEPLOYMENT_RELEASE_RETENTION_ENABLEDtrueEnables bounded terminal release pruning.
DEPLOYMENT_RELEASE_RETENTION_COUNT3Newest releases protected per environment, in addition to observed-applied and non-terminal releases.
DEPLOYMENT_RELEASE_RETENTION_BATCH_SIZE250Maximum release rows deleted per run.
DEPLOYMENT_RELEASE_RETENTION_INTERVAL_HOURS24Minimum interval between release retention runs.
DEPLOYMENT_RELEASE_RETENTION_LAST_RUN_KV_KEYcleanup:deployment-releases:last-runKV interval marker.
DEPLOYMENT_RELEASE_RECONCILIATION_ENABLEDtrueEnables stale non-terminal compose release reconciliation before terminal retention.
DEPLOYMENT_RELEASE_RECONCILIATION_BATCH_SIZE50Maximum stale non-terminal releases marked failed per retention run.
DEPLOYMENT_RELEASE_RECONCILIATION_STALE_HOURS168Minimum release status age before reconciliation can terminalize a stale non-terminal release.
DEPLOYMENT_RELEASE_RECONCILIATION_ACTIVITY_GRACE_HOURS6Recent release-event window that protects active fetch/apply work from reconciliation.
COMPOSE_IMAGE_ARTIFACT_CLEANUP_BATCH_SIZE250Maximum abandoned compose archives deleted per daily run.

Pulumi updates the existing assets bucket lifecycle resource on upgrades and creates the same rules on clean installs (infra/resources/storage.ts:r2BucketLifecycle). temp-uploads/ is transient browser-upload staging; tts/ is a regenerable audio cache. Durable library/ content is deleted only with its project, and reachable compose-image-artifacts/ are governed by deployment release retention, so neither durable prefix has an age-only lifecycle rule.

Pulumi optionDefaultObject prefixDescription
sessionSnapshotTtlDays7session-snapshots/Worker-owned retention from actual sleep; no age-only R2 lifecycle
diagnosticIncidentTtlDays7configured privatePrivate diagnostic artifact retention
tempUploadTtlDays1temp-uploads/Abandoned presigned browser upload retention
ttsTtlDays30tts/Regenerable TTS audio-cache retention

All TTL options must be positive integers. Set overrides with pulumi config set against the target stack before running its deployment workflow.

Google login and Google infrastructure authorization are independent credential families:

VariablesPurposeRuntime precedenceRedirect URIs
GOOGLE_LOGIN_CLIENT_ID, GOOGLE_LOGIN_CLIENT_SECRETBetterAuth user login/setup or superadmin runtime D1 → Worker env → unset/api/auth/callback/google
GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRETKeyless GCP/WIF setup onlySuperadmin runtime D1 → Worker env → unset/auth/google/callback and /api/deployment/gcp/callback

Configuring one family never enables or modifies the other. Users who choose service-account JSON do not need either infrastructure OAuth variable.

VariableDefaultDescription
GCP_SERVICE_ACCOUNT_JSON_MAX_BYTES65536Maximum UTF-8 byte size accepted by PUT /api/gcp/service-account
GCP_DEFAULT_ZONEus-central1-aDefault Compute zone
GCP_IMAGE_FAMILYubuntu-2404-lts-amd64Compute image family
GCP_IMAGE_PROJECTubuntu-os-cloudCompute image project
GCP_DISK_SIZE_GB50Boot disk size
GCP_TOKEN_CACHE_TTL_SECONDS3300Maximum derivative access-token cache TTL; actual TTL is capped by Google’s returned expiry
GCP_IDENTITY_TOKEN_EXPIRY_SECONDS600SAM identity-token lifetime for WIF
GCP_OPERATION_POLL_TIMEOUT_MS300000Maximum wait for GCP asynchronous operations
GCP_API_TIMEOUT_MS30000GCP OAuth, IAM, and Compute request timeout
GCP_STS_SCOPEhttps://www.googleapis.com/auth/cloud-platformWIF STS exchange scope
GCP_SA_IMPERSONATION_SCOPEShttps://www.googleapis.com/auth/computeComma-separated scopes for WIF service-account impersonation
GCP_SA_TOKEN_LIFETIME_SECONDS3600WIF impersonated access-token lifetime
GCP_STS_TOKEN_URLhttps://sts.googleapis.com/v1/tokenWIF STS endpoint override for controlled environments
GCP_IAM_CREDENTIALS_BASE_URLGoogle IAM Credentials APIWIF impersonation base URL override

The service-account JWT bearer flow always uses https://oauth2.googleapis.com/token; it has no endpoint override, and uploaded token_uri values are ignored. Source credentials are encrypted in D1. Only derivative short-lived tokens are cached.

VariableDefaultDescription
TASK_TITLE_MODEL@cf/zai-org/glm-5.2Workers AI model for title generation
TASK_TITLE_MAX_LENGTH100Max characters in generated title
TASK_TITLE_TIMEOUT_MS5000Timeout before falling back to truncation
TASK_TITLE_GENERATION_ENABLEDtrueSet false to disable AI generation
TASK_TITLE_SHORT_MESSAGE_THRESHOLD100Messages at or below this length bypass AI
TASK_TITLE_MAX_RETRIES2Max retry attempts on failure
TASK_TITLE_RETRY_DELAY_MS1000Base delay between retries (exponential backoff)
TASK_TITLE_RETRY_MAX_DELAY_MS4000Max delay cap for backoff
TASK_TITLE_ERROR_DIAGNOSTIC_MAX_LENGTH512Max sanitized provider-error diagnostic length
VariableDefaultDescription
BRANCH_NAME_PREFIXsam/Prefix for generated task output branches. Include the trailing separator (for example agent/).

Task workspaces are checked out on the generated output branch, and SAM refuses to auto-push a completed task while the workspace is still on the project’s default branch. See Where the work lands.

VariableDefaultDescription
DEBUG_AGENT_MODEL@cf/zai-org/glm-5.2Workers AI model for superadmin deployment diagnosis
DEBUG_AGENT_MAX_TURNS6Maximum model/tool turns per diagnosis
DEBUG_AGENT_RUN_TOKEN_LIMIT24000Combined token ceiling per diagnosis
DEBUG_AGENT_MODEL_OUTPUT_TOKENS4096Maximum output tokens requested per model turn
DEBUG_AGENT_DAILY_TOKEN_LIMIT120000Daily diagnosis token budget, counted per feature
DEBUG_AGENT_TOOL_RESULT_LIMIT50Maximum rows returned by a diagnosis tool
DEBUG_AGENT_TOOL_RESULT_BYTES32768Maximum serialized bytes per model-visible tool result
DEBUG_AGENT_MAX_WINDOW_HOURS24Maximum selectable diagnosis window
DEBUG_AGENT_TIMEOUT_MS120000Timeout for each diagnosis model request
DEBUG_AGENT_HARD_DEADLINE_MS900000Hard deadline for an active diagnosis
DEBUG_AGENT_STALE_HEARTBEAT_MS120000Orphan reconciler heartbeat threshold
DEBUG_AGENT_RETRY_BASE_DELAY_MS2000Initial transient step retry delay
DEBUG_AGENT_RETRY_MAX_DELAY_MS60000Maximum transient step retry delay
DEBUG_AGENT_STEP_MAX_RETRIES3Maximum classified transient retries per step

The /admin/errors view remains superadmin-only and may show local user IDs, IP addresses, and user-agent strings. Before any tool result enters model context, SAM recursively removes those fields plus credential-shaped values such as API tokens, JWTs, authorization headers, private keys, and long secret-like strings. Cloudflare credentials stay server-side and are never included in model messages or saved diagnosis text.

VM failures use a durable local SQLite outbox and a private R2 artifact. Generated deployments set the R2 prefix and object lifecycle from Pulumi; the remaining Worker bounds can be overridden through deployment environment variables.

Worker variableDefaultDescription
MAX_VM_AGENT_ERROR_BODY_BYTES32768Maximum VM error batch body
MAX_VM_AGENT_ERROR_BATCH_SIZE10Maximum errors per VM batch
MAX_VM_AGENT_ERROR_SOURCE_LENGTH256Maximum redacted VM error source length
OBSERVABILITY_ERROR_MESSAGE_MAX_LENGTH2048Maximum persisted observability error message length
OBSERVABILITY_ERROR_STACK_MAX_LENGTH4096Maximum persisted observability stack length
OBSERVABILITY_ERROR_USER_AGENT_MAX_LENGTH512Maximum persisted observability user-agent length
VM_INCIDENT_R2_PREFIXdiagnostic-incidentsPrivate object prefix; generated from the Pulumi output
VM_INCIDENT_ARTIFACT_MAX_BYTES2097152Maximum compressed artifact size
VM_INCIDENT_REGISTRATION_MAX_BYTES262144Maximum registration JSON body
VM_INCIDENT_MANIFEST_MAX_BYTES131072Maximum redacted manifest
VM_INCIDENT_PREVIEW_MAX_BYTES131072Maximum redacted model/UI preview
VM_INCIDENT_MAX_ARTIFACTS_PER_NODE50Active artifact quota per node
VM_INCIDENT_MAX_BYTES_PER_NODE104857600Active expected-byte quota per node
VM_INCIDENT_RETENTION_DAYS7Private object and active metadata retention
VM_INCIDENT_METADATA_RETENTION_DAYS30Expired metadata retention after object deletion
VM_INCIDENT_PENDING_TIMEOUT_MINUTES30Incomplete-upload timeout and upload-lease duration
VM_INCIDENT_RECONCILE_BATCH_SIZE50Maximum artifacts/incidents repaired per scheduled pass (minimum: 6)

The VM Agent process accepts the corresponding ERROR_REPORT_* overrides for flush interval, batch size/bytes, outbox size and path, SQLite busy timeout, HTTP timeout, retry bounds, attempts, spool path/bytes, artifact bytes, retention, collector timeout/count/concurrency, document bytes, recursive value depth/items, string bytes, structured event limit, response-read bytes, and persisted-error bytes. Generated deployments pass these validated values through cloud-init into the VM Agent systemd service, so overrides apply to newly provisioned nodes. Defaults are listed in apps/api/.env.example; the common defaults are a 32 KiB error batch, 1,000-row outbox, 2 MiB artifact, 20 MiB spool, and 24-hour local retention.

Pulumi options diagnosticIncidentPrefix (default diagnostic-incidents) and diagnosticIncidentTtlDays (default 7, any positive integer) configure the private prefix and an independent R2 lifecycle rule. They do not require a separate bucket or manually managed Worker variable. The prefix cannot begin with the application-owned namespaces agents, cli, compose-image-artifacts, library, session-snapshots, temp-uploads, or tts, because the lifecycle would otherwise expire unrelated objects.

VariableDefaultDescription
PLATFORM_FEEDBACK_PROJECT_IDunsetProject that receives user issue reports and automated triage draft Ideas; unset ⇒ in-app reporting hidden
PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES60Lookback window for grouping recent platform errors
PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT100Maximum platform error rows scanned per triage sweep
PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT5Maximum grouped feedback candidates processed per triage sweep
PLATFORM_FEEDBACK_TRIAGE_EVIDENCE_LIMIT10Maximum bounded error references retained per grouped feedback record
PLATFORM_FEEDBACK_TRIAGE_CLAIM_TTL_MS600000Claim lease duration before a later sweep can reclaim the group
PLATFORM_FEEDBACK_TRIAGE_MAX_FAILURES3Maximum failed attempts before a group is rejected from auto-triage
PLATFORM_FEEDBACK_TRIAGE_FAILURE_REASON_MAX_LENGTH240Maximum characters stored or returned for sanitized failure reasons

Automated triage and superadmin-initiated diagnosis read the same DEBUG_AGENT_DAILY_TOKEN_LIMIT value but count against independent per-feature counters, so worst-case daily spend across both is twice this value.

The in-app Report an Issue flow files user-submitted reports as draft Ideas in PLATFORM_FEEDBACK_PROJECT_ID (above). The feature is hidden entirely — both UI entry points disappear and GET /api/report-issue/config returns enabled: false — when that variable is unset or points at a project that does not exist in this deployment’s database.

VariableDefaultDescription
REPORT_ISSUE_TITLE_MAX_LENGTH200Truncation ceiling for the stored title (lowers only — see below)
REPORT_ISSUE_DESCRIPTION_MAX_LENGTH5000Truncation ceiling for the stored description (lowers only)
REPORT_ISSUE_CONTENT_MAX_LENGTH65536Maximum stored Idea body, including attached technical references
RATE_LIMIT_REPORT_ISSUE_POST20Report submissions allowed per clock hour, per authenticated user

The two length variables apply after request validation, so they can only lower the stored length — the request schema and the report dialog both enforce the built-in 200 / 5,000 caps regardless of what you set here.

See Reporting Issues for the user-facing flow and the untrusted-evidence Idea format.

SAM loads OpenCode Zen and OpenCode Go model choices through the authenticated model-catalog API, backed by Models.dev and cached in KV. If the upstream catalog or cache is unavailable, SAM falls back to the static catalog shipped with the app.

VariableDefaultDescription
MODEL_CATALOG_SOURCE_URLhttps://models.dev/api.jsonSource URL for the dynamic model catalog
MODEL_CATALOG_CACHE_TTL_SECONDS3600KV cache TTL for normalized dynamic model catalog payloads
MODEL_CATALOG_FETCH_TIMEOUT_MS5000Timeout for the upstream catalog fetch before static fallback

Conservative Cache-Control budgets for stable and semi-stable API GETs, letting the browser serve a cached body instantly while it revalidates in the background. All values are seconds and are clamped to [0, 86400]; an unparseable or negative value falls back to the default rather than caching for longer.

Authenticated responses are always emitted as private with Vary: Cookie, so neither a shared cache nor a second account in the same browser can be served another user’s body. Only the unauthenticated /api/config/* endpoints are marked public. Endpoints returning real-time data (chat messages, task status, session and workspace state) are deliberately excluded.

VariableDefaultDescription
PUBLIC_CONFIG_CACHE_MAX_AGE_SECONDS60max-age for the unauthenticated /api/config/* endpoints
PUBLIC_CONFIG_CACHE_SWR_SECONDS300stale-while-revalidate for /api/config/*
MODEL_CATALOG_CACHE_MAX_AGE_SECONDS60max-age for GET /api/model-catalog/:agentType
MODEL_CATALOG_CACHE_SWR_SECONDS300stale-while-revalidate for the model catalog response
PROJECT_REFERENCE_CACHE_MAX_AGE_SECONDS0max-age for project agent-profile and skill lists (0 = always revalidate)
PROJECT_REFERENCE_CACHE_SWR_SECONDS30stale-while-revalidate for project agent-profile and skill lists
VariableDefaultDescription
NODE_WARM_TIMEOUT_MS1800000 (30 min)Time a node stays warm after idea execution completes
MAX_AUTO_NODE_LIFETIME_MS14400000 (4 hr)Max lifetime for an auto-provisioned node holding no active workspaces
NODE_WARM_GRACE_PERIOD_MS2100000 (35 min)Cron sweep grace period (must be > warm timeout)
NODE_LIFECYCLE_ALARM_RETRY_MS60000 (1 min)Retry delay for DO alarm failures
NODE_LIFECYCLE_MAX_DESTROYING_AGE_MS86400000 (24 hr)Backstop after which a destroying-state alarm self-cleans; infrastructure teardown remains owned by cron/provider reconciliation
DEFAULT_TASK_AGENT_TYPEopencodeDefault agent for autonomous idea execution

The cleanup sweep measures idleness from a node’s last workspace activity (COALESCE(MAX(workspaces.updated_at), nodes.created_at)), never from nodes.updated_at — heartbeats rewrite updated_at on every beat, so it tracks liveness rather than idleness.

Reaping only ever applies to nodes with node_role = 'workspace' and node_class != 'user-owned'. Deployment nodes host long-running user applications and legitimately hold zero workspaces forever, so they are never reaped by these timers; they are released when their last deployment environment is deleted.

VariableDefaultDescription
NODE_ORPHAN_IDLE_TIMEOUT_MS2700000 (45 min)Idle window before a running workspace node with no active workspaces is destroyed, and minimum pre-heartbeat grace before an unversioned, unclaimed workspace VM can be retired. Keep above NODE_WARM_TIMEOUT_MS so the warm path reclaims reusable nodes first.
NODE_ABSOLUTE_MAX_LIFETIME_MS86400000 (24 hr)Hard ceiling on auto-provisioned workspace node age. Applies even when a workspace row still reports running, provided no workspace has reported activity within the idle window — this is what stops a stuck workspace row from making a node immortal.
NODE_CLEANUP_SWEEP_LIMIT25Max node candidates processed per cleanup phase per cron run.
NODE_CLEANUP_FAILURE_BACKOFF_MS3600000 (1 hr)Expiring exclusion applied to failed cleanup candidates so a permanent provider error cannot monopolize the bounded page.
WORKSPACE_CLEANUP_SWEEP_LIMIT50Max workspace candidates processed per cleanup phase per cron run.
NODE_AGENT_BACKGROUND_REQUEST_TIMEOUT_MS5000 (5 s)VM-agent request timeout for background sweeps. Deliberately far below the interactive NODE_AGENT_REQUEST_TIMEOUT_MS (30 s) so a sweep over unreachable nodes cannot exhaust the Worker’s wall-clock budget.

The cron and Durable Object switches are availability brakes: an absent key or KV read error means enabled (fail-open). This differs deliberately from the fail-closed trials entitlement switch. Superadmins can inspect and update both brakes through /api/admin/runtime-controls; emergency operators can use the KV procedure in .claude/rules/55-runaway-cost-emergency-ops.md.

VariableDefaultDescription
CRON_SWEEPS_ENABLED_KV_KEYcontrol-loops:cron-enabledKV key gating the five-minute operational sweep block
DO_ALARMS_ENABLED_KV_KEYcontrol-loops:alarms-enabledShared KV key gating alarm-bearing Durable Objects
CONTROL_LOOP_KILL_SWITCH_CACHE_MS30000In-memory switch cache; runtime clamps it to at most 30 seconds
CONTROL_LOOP_DISABLED_ALARM_RETRY_MS300000 (5 min)Safe alarm recheck interval while DO work is disabled; values below 60 seconds are clamped
CRON_FAILURE_NOTIFICATION_THROTTLE_MS3600000 (1 hr)Per-sweep throttle enforced by a KV cache plus an atomic per-user Notification DO claim
CRON_FAILURE_NOTIFICATION_KV_PREFIXcron-failure-notificationKV prefix for notification throttle markers
DIAGNOSIS_COMPLETED_STEP_MIN_DELAY_MS1000Minimum delayed re-arm for an already-completed diagnosis step
ORCHESTRATOR_ZERO_TASK_GRACE_MS600000 (10 min)Grace period before an active mission with no tasks terminalizes
ORCHESTRATOR_MAX_MISSION_LIFETIME_MS86400000 (24 hr)Backstop that force-completes active/completing missions

The scheduled Durable Object billing monitor reads these non-secret variables from the selected GitHub Environment, not from the API Worker runtime:

VariableDefault/fallbackDescription
DO_WALL_TIME_SCRIPT_NAMESnoneOptional comma-separated API Worker filter for wall-time and invocation-rate analysis
DO_INVOCATION_RATE_REGRESSION_RATIO2Recent-versus-seven-day-baseline request-rate failure ratio
DO_CRON_LIVENESS_MAX_AGE_HOURS3Maximum age of the most recent targeted cron.completed event
DO_CRON_LIVENESS_SCRIPT_NAMESDO_WALL_TIME_SCRIPT_NAMESExplicit API Worker service target for cron liveness; the GitHub workflow derives both from RESOURCE_PREFIX and the selected stack when unset
DO_CRON_LIVENESS_ENDPOINTCloudflare Workers Observability query endpointOptional endpoint override for compatible/private telemetry gateways

The selected GitHub Environment’s CF_API_TOKEN secret must include the Cloudflare Workers Observability Write permission. Cloudflare requires that permission for the telemetry query endpoint even though this monitor only reads aggregated liveness telemetry.

Reclaims cloud servers that exist at the provider but which no live database row claims — for example when a server was created but the control plane failed before recording its instance ID.

Because this is the only path that destroys infrastructure on the basis of absent evidence, it fails closed at every step. A server must carry both the current control-plane env value and the exact Pulumi-generated installation marker before SAM consults D1. SAM then re-reads and revalidates the same provider resource immediately before it calls the provider delete API. Provider-account membership, server names, resource prefixes, and absence from this installation’s D1 are not ownership proof.

Pulumi generates the non-secret installation identity automatically on first deploy, persists it in the stack state, and injects it into the Worker as SAM_INSTALLATION_ID; there is no manual GitHub Environment setting. An upgrade does not relabel existing servers. Legacy servers without the marker remain usable and are preserved indefinitely, while servers provisioned after the upgrade participate in normal orphan cleanup. If the Pulumi state is lost or recreated, the new identity safely leaves the old fleet unattributable instead of adopting it destructively. Any missing/malformed identity, ambiguous provider metadata, or failed/malformed D1 lookup skips deletion. Resources surfaced to reconciliation with non-owning metadata emit aggregate operator-visible counters.

VariableDefaultDescription
PROVIDER_ORPHAN_RECONCILIATION_ENABLEDtrueSet to false to disable provider-side reconciliation entirely.
PROVIDER_ORPHAN_MIN_AGE_MS3600000 (1 hr)Minimum server age before it can be treated as an orphan. Must comfortably exceed provisioning time, since a server’s instance ID is recorded only after the provider returns it.
PROVIDER_ORPHAN_DESTROY_LIMIT5Max servers destroyed per reconciliation run.
PROVIDER_ORPHAN_RECONCILE_INTERVAL_MS3600000 (1 hr)Minimum interval between runs. Invoked by the 5-minute cron but self-throttled to this interval via KV.
VariableDefaultDescription
PROJECT_INVITE_TOKEN_BYTES32Random bytes used for generated project invite link tokens
PROJECT_INVITE_DEFAULT_EXPIRY_DAYS7Default lifetime for invite links created without an explicit expiry
PROJECT_INVITE_MAX_EXPIRY_DAYS30Maximum allowed invite link lifetime, including explicit expiry-date input
PROJECT_OFFBOARDING_PLAN_TTL_SECONDS900Lifetime for project member offboarding preview plans before recomputation
VariableDefaultDescription
NOTIFICATION_PROGRESS_BATCH_WINDOW_MS300000 (5 min)Min interval between progress notifications per idea
NOTIFICATION_DEDUP_WINDOW_MS60000 (60s)Dedup window for task_complete notifications
NOTIFICATION_AUTO_DELETE_AGE_MS7776000000 (90 days)Auto-delete old notifications
MAX_NOTIFICATIONS_PER_USER500Max stored notifications per user
NOTIFICATION_PAGE_SIZE50Default page size for notification list
MAX_NOTIFICATION_PAGE_SIZE100Max allowed page size
HUMAN_INPUT_TIMEOUT_MS7200000 (2 hr)Initial needs-input response window
HUMAN_INPUT_ESCALATION_FRACTIONS0.25,0.75Reminder points within the initial response window
HUMAN_INPUT_UNDELIVERED_GRACE_MS7200000 (2 hr)Extension without confirmed push delivery
HUMAN_INPUT_MAX_WAIT_MS86400000 (24 hr)Hard maximum needs-input marker lifetime
WEB_PUSH_TTL_SECONDS86400Push-service message TTL
WEB_PUSH_VAPID_TTL_SECONDS43200VAPID authorization-token lifetime
WEB_PUSH_DELIVERY_TIMEOUT_MS10000Per-attempt push-service timeout
WEB_PUSH_DELIVERY_BUDGET_MS25000Total fan-out budget, hard-capped at 25s below Worker background limit
WEB_PUSH_FANOUT_CONCURRENCY8Maximum concurrent endpoint deliveries
WEB_PUSH_MAX_ATTEMPTS3Bounded transient delivery attempts
WEB_PUSH_MAX_RETRY_AFTER_SECONDS30Maximum honored Retry-After delay
WEB_PUSH_MAX_PAYLOAD_BYTES3500Maximum unencrypted payload size
WEB_PUSH_FAILURE_THRESHOLD5Consecutive failures before disabling a subscription
WEB_PUSH_MAX_SUBSCRIPTIONS_PER_USER8Maximum retained browser endpoints per user
WEB_PUSH_USER_AGENT_MAX_LENGTH512Maximum stored browser description length
RATE_LIMIT_PUSH_SUBSCRIPTION30Subscription mutations per user per hour
VariableDefaultDescription
WEBHOOK_TRIGGERS_ENABLEDtruePublic generic webhook ingress kill switch
WEBHOOK_TRIGGER_MAX_BODY_BYTES65536Maximum JSON request body size
WEBHOOK_TRIGGER_MAX_FILTERS10Maximum deterministic filters per trigger
WEBHOOK_TRIGGER_MAX_FILTER_PATH_LENGTH200Maximum configured filter dot-path length
WEBHOOK_TRIGGER_MAX_FILTER_PATH_DEPTH8Maximum filter nesting depth at evaluation time
WEBHOOK_TRIGGER_MAX_INCLUDED_HEADERS10Maximum safe request headers copied into template context
WEBHOOK_TRIGGER_MAX_HEADER_NAME_LENGTH100Maximum configured included-header name length
WEBHOOK_TRIGGER_MAX_SOURCE_LABEL_LENGTH100Maximum optional source label length
WEBHOOK_TRIGGER_MAX_IDEMPOTENCY_KEY_LENGTH200Maximum accepted Idempotency-Key length
WEBHOOK_INGRESS_RATE_LIMIT_PER_MINUTE120Best-effort pre-auth request damping per client IP/window
WEBHOOK_TRIGGER_RATE_LIMIT_PER_MINUTE60Best-effort request damping per trigger/window
WEBHOOK_INVALID_TOKEN_RATE_LIMIT_PER_MINUTE30Best-effort invalid-token damping per client IP/window
WEBHOOK_RATE_LIMIT_WINDOW_SECONDS60Fixed rate-limit window length
WEBHOOK_DELIVERY_RETENTION_DAYS7Retention for redacted delivery audit metadata
WEBHOOK_DELIVERY_CLEANUP_BATCH_SIZE500Maximum expired audit rows deleted per cleanup pass
WEBHOOK_DELIVERY_DEFAULT_PAGE_SIZE25Default delivery-history page size
WEBHOOK_DELIVERY_MAX_PAGE_SIZE100Maximum delivery-history page size
WEBHOOK_DELIVERY_PROCESSING_LEASE_SECONDS300Lease before an unsubmitted processing delivery can recover

Webhook tokens use the existing ENCRYPTION_KEY as keyed-hash material and do not require a separate deployment secret. See Webhook Triggers for request, credential, filtering, and audit behavior.

Webhook damping uses Cloudflare KV’s eventually consistent read-update-write behavior. It reduces accidental bursts and abuse but is not a strict distributed quota.

VariableDefaultDescription
ACP_SESSION_DETECTION_WINDOW_MS300000 (5 min)Heartbeat timeout before marking session interrupted
ACP_SESSION_HEARTBEAT_INTERVAL_MS60000 (60s)How often VM agent sends heartbeats
ACP_SESSION_RECONCILIATION_TIMEOUT_MS30000 (30s)VM agent startup reconciliation timeout
ACP_SESSION_MAX_FORK_DEPTH10Maximum session fork chain depth
ACP_SESSION_FORK_CONTEXT_MESSAGES20Context messages included when forking
VariableDefaultDescription
ACP_MESSAGE_BUFFER_SIZE5000Buffer size for ACP messages
ACP_STDERR_BUFFER_BYTES4096Agent stderr bytes retained for crash reports
ACP_PING_INTERVAL30sWebSocket keepalive ping interval
ACP_PONG_TIMEOUT10sPong response timeout
ACP_TASK_PROMPT_TIMEOUT8hTask execution prompt timeout
ACP_PROMPT_RETRY_MAX_RETRIES2Max transient provider prompt retries after the initial attempt
ACP_PROMPT_RETRY_INITIAL_BACKOFF15sInitial backoff before retrying transient provider prompt errors
ACP_PROMPT_RETRY_MAX_BACKOFF2mMax exponential backoff for transient provider prompt retries
ACTIVITY_REREPORT_INTERVAL60sRe-send prompting activity while a prompt is active
ACP_CHECKPOINT_PREEMPT_GRACE30sGraceful ACP cancel/close wait before harness force-stop
ACP_CHECKPOINT_PREEMPT_MAX_GRACE2mMaximum caller-selected checkpoint rollover grace
ACP_CHECKPOINT_ROLLOVER_TIMEOUT2mFull checkpoint restart and strict LoadSession deadline
ACTIVITY_TERMINAL_REPORT_ATTEMPTS5Retry attempts for terminal activity reports
ACTIVITY_TERMINAL_REPORT_BACKOFF1sBackoff between terminal activity report retries
ACP_IDLE_SUSPEND_TIMEOUT30mIdle session auto-suspend timeout
ACP_NOTIF_SERIALIZE_TIMEOUT5sNotification serialization timeout
VariableDefaultDescription
MCP_TOKEN_TTL_SECONDS28800 (8 hours)Sliding inactivity timeout for agent MCP access
MCP_RATE_LIMIT120Max MCP requests per window
MCP_RATE_LIMIT_WINDOW_SECONDS60Rate limit window
MCP_DISPATCH_MAX_DEPTH3Max recursion depth for dispatch_task
MCP_DISPATCH_MAX_PER_TASK5Max dispatched tasks per parent task
MCP_DISPATCH_MAX_ACTIVE_PER_PROJECT10Max active dispatched tasks per project
ORCHESTRATOR_STOP_CAS_MAX_ATTEMPTS2Task-status CAS attempts after a hard stop
VariableDefaultDescription
WHISPER_MODEL_ID@cf/openai/whisper-large-v3-turboTranscription model
MAX_AUDIO_SIZE_BYTES10485760 (10 MB)Max upload audio size
MAX_AUDIO_DURATION_SECONDS60Max recording duration
RATE_LIMIT_TRANSCRIBE30Max transcriptions per minute
TTS_ENABLEDtrueEnable/disable text-to-speech
TTS_MODEL@cf/deepgram/aura-2-enTTS model
TTS_SPEAKERlunaTTS voice selection
TTS_ENCODINGmp3Audio output format
TTS_MAX_TEXT_LENGTH100000Max characters per TTS synthesis
TTS_TIMEOUT_MS60000TTS synthesis timeout
VariableDefaultDescription
CONTEXT_SUMMARY_MODEL@cf/google/gemma-4-26b-a4b-itModel for conversation context summarization
CONTEXT_SUMMARY_MAX_LENGTH4000Max summary length in characters
CONTEXT_SUMMARY_TIMEOUT_MS10000Summarization timeout
CONTEXT_SUMMARY_MAX_MESSAGES50Max messages to include in summary
CONTEXT_SUMMARY_SHORT_THRESHOLD5Skip AI for conversations this short
VariableDefaultDescription
TASK_RUN_MAX_EXECUTION_MS14400000 (4 hr)Max task execution time
TASK_STUCK_QUEUED_TIMEOUT_MS1200000 (20 min)Timeout for tasks stuck in queued state
TASK_STUCK_DELEGATED_TIMEOUT_MS1860000 (31 min)Timeout for tasks stuck in delegated state
TASK_DO_MISMATCH_GRACE_MS300000 (5 min)Minimum age before reconciling completed TaskRunner state with task-scoped liveness
STUCK_TASK_MAX_CANDIDATES_PER_SWEEP100Maximum active tasks inspected by each recovery sweep
STUCK_TASK_SCAN_CURSOR_KV_KEYscheduled:stuck-tasks:scan-cursor:v1KV key used to resume bounded recovery scans fairly across active tasks
TASK_LIVENESS_MAX_ACP_SESSIONS5Maximum task-scoped ACP sessions inspected per liveness probe
TASK_LIVENESS_PROBE_TIMEOUT_MS5000 (5 sec)Per-candidate timeout for ACP and Instant lifecycle probes used by ProjectData heartbeat deferral, idle cleanup, and stuck-task reconciliation; a timeout is inconclusive and preserves the task and workspace
IDLE_CLEANUP_MAX_CANDIDATES_PER_SWEEP5Maximum exact-session task candidates inspected by a ProjectData idle-cleanup pass; workspace deletion is deferred when this bound cannot prove every reporter-scoped runtime conclusively dead
IDLE_CLEANUP_MAX_RESIDENCE_MS7200000 (2 hr)Maximum residence for a ProjectData idle-cleanup schedule before repeated preserved/error outcomes stop re-arming, preserve the workspace, and surface an attention marker
TASK_RUN_ABSOLUTE_CEILING_MS86400000 (24 hr)Absolute runaway-cost ceiling; fails even a task with a demonstrably live runtime
CLAUDE_CODE_COMPACTION_LOOP_DETECTOR_ENABLEDtrueEnable Claude Code compaction-loop shutdown from recent message evidence
CLAUDE_CODE_COMPACTION_LOOP_RECENT_MESSAGE_LIMIT40Recent task-session messages to inspect for compaction-loop evidence
CLAUDE_CODE_COMPACTION_LOOP_WINDOW_MESSAGES20Rolling recent-message window used for compaction-loop detection
CLAUDE_CODE_COMPACTION_LOOP_MIN_PAIRS3Minimum Compacting... / Compacting completed marker pairs before failing a task
TASK_CALLBACK_TIMEOUT_MS10000Callback response timeout
TASK_CALLBACK_RETRY_MAX_ATTEMPTS3Max callback retry attempts
TASK_RUN_CLEANUP_DELAY_MS5000Delay before task cleanup
TASK_RECONCILIATION_IDLE_MS300000 (5 min)Idle threshold before SAM sends a visible task check-in
TASK_RECONCILIATION_RESPONSE_DEADLINE_MS60000 (1 min)Response deadline after a visible task check-in
TASK_RECONCILIATION_PROMPT_SOFT_STALL_MS1800000 (30 min)In-flight prompt observation threshold before a non-interrupting reconciliation event
TASK_RECONCILIATION_PROMPT_HARD_STALL_MS7200000 (2 hr)In-flight prompt hard-stall threshold before SAM requests prompt cancellation
TASK_RECONCILIATION_MIN_ALARM_DELAY_MS10000 (10 sec)Minimum delay before the next reconciliation alarm can fire
INSTANT_START_STALE_TIMEOUT_MS600000 (10 min)How long an Instant session may sit mid-launch (execution step instant_persistence) before the recovery sweep treats its start as stuck and fails it. Instant starts are accepted and then finished in the background, so this bounds a launch that never completes.

Durable prompt delivery and checkpoint storage

Section titled “Durable prompt delivery and checkpoint storage”

Durable prompt delivery is enabled by default so a follow-up can remain queued while a sleeping VM is replaced and restored. Legacy VM compatibility remains disabled: targets must advertise stable delivery receipts, and receipt ambiguity fails visibly rather than being guessed or replayed.

VariableDefaultDescription
DURABLE_PROMPT_DELIVERY_ENABLEDtruePersist prompts and deliver them from ProjectData alarms, including sleeping-session wake.
PROMPT_DELIVERY_LEGACY_VM_COMPAT_ENABLEDfalseExplicit old-VM compatibility switch; receipt ambiguity still fails visibly and is never guessed or replayed.
PROMPT_DELIVERY_MAX_CANDIDATES_PER_ALARM5Maximum delivery claims started by one alarm pass.
PROMPT_DELIVERY_MAX_ATTEMPTS5Counted delivery attempts before retryable busy/not-ready waits use capped backoff; TTL remains the hard bound.
PROMPT_DELIVERY_RETRY_BASE_MS5000Initial retry delay.
PROMPT_DELIVERY_RETRY_MAX_MS300000Maximum exponential retry delay.
PROMPT_DELIVERY_TTL_MS3600000Maximum unresolved delivery lifetime.
PROMPT_DELIVERY_RECEIPT_TIMEOUT_MS30000Age at which an unconfirmed claim enters receipt reconciliation.
PROMPT_DELIVERY_BACKGROUND_TIMEOUT_MS5000Timeout for background VM delivery and receipt calls.
PROMPT_DELIVERY_MIN_ALARM_DELAY_MS1000Minimum delay before the next delivery alarm.
ACP_LONG_TURN_SUPERVISOR_ENABLEDfalseReserved long-turn candidate/preemption engine switch; this release leaves it inert.
ACP_LONG_TURN_CHECKPOINT_MS18000000 (5 hr)Reserved checkpoint eligibility threshold.
ACP_CHECKPOINT_PREEMPT_GRACE_MS30000Reserved graceful preemption window.
ORCHESTRATOR_WAIT_RECONCILE_INTERVAL_MS30000D1 reconciliation backstop interval for active parent waits.
ORCHESTRATOR_WAIT_MAX_CHILDREN20Maximum direct children selected by one durable wait (hard ceiling: 90, preserving D1 bind headroom).
ORCHESTRATOR_WAIT_MAX_ACTIVE_PER_PROJECT100Maximum active durable parent waits per project.
ORCHESTRATOR_WAIT_MAX_DURATION_MS86400000Maximum finite wait deadline.
ORCHESTRATOR_WAIT_MAX_CANDIDATES_PER_ALARM10Maximum wait subscriptions reconciled by one ProjectData alarm.

ProjectData stores a single prompt-delivery queue and checkpoint episodes keyed by ACP session and prompt epoch. Sleeping-session prompts stay in that queue until strict restore succeeds, then use stable receipts for exactly-once acceptance. Task agents can register wait_for_subtasks for direct children; terminal hooks provide low-latency nudges, bounded alarms reconcile missed writers, and one stable delivery ID wakes the parent exactly once. Automatic checkpoint preemption remains disabled.

Liveness-gated recovery. Stuck-task recovery for in_progress tasks (including task-mode work paused at the awaiting_followup execution step) is gated on task-scoped liveness — a live workspace, a healthy node with a recent heartbeat, and an active task-scoped ACP session. A shared-node heartbeat alone is never sufficient. Consequently, TASK_RUN_HARD_TIMEOUT_MS and TASK_RUN_MAX_EXECUTION_MS bound the point at which a task with no proven live runtime is failed; a task with a demonstrably live runtime is preserved past those thresholds, but remains bounded by TASK_RUN_ABSOLUTE_CEILING_MS (24 hours by default) as a runaway-cost backstop. When liveness cannot be determined (probe timeout or error), the task is left untouched (fail-safe) until it reaches that absolute ceiling.

VariableDefaultDescription
NODE_AGENT_READY_TIMEOUT_MS900000 (15 min)Wait for VM agent to report ready
NODE_AGENT_READY_POLL_INTERVAL_MS5000Poll interval for agent readiness
VM_AGENT_REQUIRED_VERSION(deploy-generated)Required vm-agent build for reusable VM nodes. Official deploys derive this from the Git commit SHA after publishing matching binaries; leave unset only for local/manual development or skip-agent deploys.
TASK_RUNNER_WORKSPACE_READY_TIMEOUT_MS1800000 (30 min)Max wait for workspace-ready callback
PROVISIONING_TIMEOUT_MS1800000 (30 min)Cron marks stuck workspaces as error
NODE_HEARTBEAT_STALE_SECONDS180Seconds without a heartbeat before a node is treated as stale
VariableDefaultDescription
DEPLOY_PAYLOAD_EXPIRY_SECONDS3600Signed deployment apply payload lifetime
DEPLOYMENT_ROUTE_PORT_BASE35000First node-local loopback port reserved for app routes
DEPLOYMENT_ROUTE_PORT_SPAN100Number of loopback ports reserved per deployment environment
AGENT_DEPLOYMENT_RESERVED_ENVIRONMENT_NAMESprod,productionComma-separated environment names agents cannot create through MCP
MAX_ENVIRONMENTS_PER_DEPLOYMENT_NODE5Maximum deployment environments to place on one deployment node
DEPLOYMENT_DEFAULT_VM_SIZEsmallDefault VM size for deployment nodes
DEPLOYMENT_MODEL_RUNNER_VM_SIZEmediumVM size for deployment nodes that need Docker Model Runner
DEPLOYMENT_DEFAULT_MEMORY_LIMIT_MB256Default per-service memory limit for compose-publish releases
DEPLOYMENT_LOG_MAX_SIZE10mDefault json-file log max-size for compose-publish releases
DEPLOYMENT_LOG_MAX_FILE3Default json-file log max-file for compose-publish releases
MCP_DEPLOYMENT_COMPOSE_PREVIEW_MAX_BYTES128000Max Compose YAML size accepted by deployment route preview MCP tool
BUILD_PUBLISH_TOOL_TIMEOUT_MS1260000Worker-to-VM proxy timeout for build_and_publish
DEPLOY_ACME_EMAIL(unset)Optional ACME contact email emitted into deployment-node Caddy config
DEPLOY_ACME_CA(unset)Optional ACME CA directory override, useful for Let’s Encrypt staging
DOH_RESOLVER_URLhttps://cloudflare-dns.com/dns-queryDNS-over-HTTPS resolver used to verify deployment custom domains
DOH_TIMEOUT_MS10000Timeout for deployment custom-domain DNS verification lookups
DEPLOY_COMPOSE_CMDdocker composeDocker Compose command used by the deployment engine
DEPLOY_HEALTH_TIMEOUT5mDeployment health-check timeout used by the VM agent
DEPLOY_RUNTIME_TIMEOUT15mVM-agent max time for deployment-node host dependency setup
GRACEFUL_SHUTDOWN_TIMEOUT30sVM-agent max time for graceful HTTP server shutdown after SIGTERM
SYSTEM_PROVISIONING_TIMEOUT15mVM-agent max time for workspace host provisioning before bootstrap
CF_IP_FETCH_TIMEOUT10sVM-agent timeout for Cloudflare IP range fetches during provisioning
BOOT_LOG_HTTP_TIMEOUT10sVM-agent timeout for boot-log callbacks to the control plane
MCP_SHORT_COMMAND_TIMEOUT10sVM-agent timeout for short MCP workspace command probes
MCP_DIFF_COMMAND_TIMEOUT30sVM-agent timeout for MCP diff-summary git commands
MCP_BUILD_PREPARE_TIMEOUT30sVM-agent timeout for MCP build/publish preparation probes
JWKS_FETCH_TIMEOUT10sVM-agent startup JWKS fetch timeout
ACP_CREDENTIAL_SYNC_TIMEOUT10sVM-agent ACP auth-file sync-back timeout during shutdown
ACP_ACTIVITY_REPORT_TIMEOUT10sVM-agent timeout for each ACP activity callback attempt
DEVCONTAINER_CACHE_PUSH_TIMEOUT10mVM-agent best-effort devcontainer cache image push timeout
DEPLOY_PREFLIGHT_COMMAND_TIMEOUT15sVM-agent deployment preflight diagnostic command timeout
LOG_STREAM_PING_WRITE_TIMEOUT10sVM-agent log-stream WebSocket ping write deadline
DEPLOY_TEARDOWN_TIMEOUT2mVM-agent max time for deployment environment teardown (stop/start)
DEPLOY_APPLY_IDLE_TIMEOUT15mVM-agent idle watchdog for deployment apply (no-progress only)
DEPLOY_BUILD_PUBLISH_TIMEOUT20mVM-agent max time for host build + push + release publish
DEPLOY_ARTIFACT_DIAL_TIMEOUT30sVM-agent TCP dial timeout for artifact downloads
DEPLOY_ARTIFACT_TLS_HANDSHAKE_TIMEOUT15sVM-agent TLS handshake timeout for artifact downloads
DEPLOY_ARTIFACT_RESPONSE_HEADER_TIMEOUT60sVM-agent first-response-header timeout for artifact downloads
DEPLOY_ARTIFACT_IDLE_TIMEOUT2mVM-agent idle watchdog for artifact body-read progress
VariableDefaultDescription
MAX_NODES_PER_USER10Max nodes per user
MAX_WORKSPACES_PER_NODE3Max workspaces packed onto one node
MAX_AGENT_SESSIONS_PER_WORKSPACE10Max concurrent agent sessions
MAX_PROJECTS_PER_USER100Max projects per user
MAX_TASKS_PER_PROJECT10000Max ideas per project
MAX_TASK_MESSAGE_LENGTH16000Max idea description length
VariableDefaultDescription
MAX_SESSIONS_PER_PROJECT10000Max chat sessions per project
MAX_MESSAGES_PER_SESSION100000Max messages per chat session
DOCUMENT_CARD_RAW_OUTPUT_MAX_BYTES16384Max compact metadata bytes preserved for library document cards
MESSAGE_SIZE_THRESHOLD102400Max message size in bytes
ACTIVITY_RETENTION_DAYS90Days to retain activity events
SESSION_IDLE_TIMEOUT_MINUTES60Idle session timeout
SESSION_ACTIVITY_STALE_THRESHOLD_MS300000 (5 min)Evidence threshold before stale working activity can be healed to idle
SESSION_ACTIVITY_PROBE_TIMEOUT_MS5000 (5 s)Timeout for the vm-agent session-activity probe. Background control-loop budget — deliberately far below the interactive node-agent timeout
SESSION_ACTIVITY_PROBE_MAX_ATTEMPTS3Consecutive unreachable probes after which a stale working state is terminalized as dead
SESSION_ACTIVITY_PROBE_MAX_CANDIDATES10Stale-activity candidates probed per ProjectData alarm pass
DO_SUMMARY_SYNC_DEBOUNCE_MS5000Debounce for DO-to-D1 summary sync
SESSION_INDEX_MAX_ROWS1000Sessions mirrored into the D1 session_summaries index per project. A project holding more is recorded as incomplete and its chat sidebar reads fall back to the Durable Object
SESSION_INDEX_MAX_STALENESS_MS900000 (15 min)How stale the session index may be before the per-project sidebar list stops trusting it and falls back to the Durable Object
VariableDefaultDescription
DO_RETRY_MAX_ATTEMPTS8Max attempts for transient Durable Object RPC reset/overload errors
DO_RETRY_BASE_DELAY_MS100Base retry delay in milliseconds for transient Durable Object RPC failures
DO_RETRY_MAX_DELAY_MS250Max per-attempt retry delay for transient Durable Object RPC failures
PROJECT_DATA_ENSURE_MEMO_MAX_ENTRIES2000Max ProjectData Durable Objects one Worker isolate remembers as already having a persisted projectId, so ensureProjectId costs one RPC per isolate instead of one before every DO call
VariableDefaultDescription
MAX_PROJECT_RUNTIME_ENV_VARS_PER_PROJECT150Max env vars per project
MAX_PROJECT_RUNTIME_FILES_PER_PROJECT50Max files per project
MAX_PROJECT_RUNTIME_ENV_VALUE_BYTES8192Max bytes per env var value
MAX_PROJECT_RUNTIME_FILE_CONTENT_BYTES131072Max bytes per file content
MAX_PROJECT_RUNTIME_FILE_PATH_LENGTH256Max file path length
MAX_DEPLOYMENT_ENV_VARS_PER_ENVIRONMENT100Max deployment config vars per environment
MAX_DEPLOYMENT_ENV_VALUE_BYTES65536Max bytes per deployment config value
MAX_DEPLOYMENT_ENV_TOTAL_BYTES262144Max aggregate deployment config env size
VariableDefaultDescription
HETZNER_API_TIMEOUT_MS30000Hetzner API request timeout
CF_API_TIMEOUT_MS30000Cloudflare API request timeout
GCP_API_TIMEOUT_MS30000GCP OAuth, IAM, and Compute request timeout
NODE_AGENT_REQUEST_TIMEOUT_MS30000VM Agent request timeout
DIGITALOCEAN_API_TIMEOUT_MS30000DigitalOcean API request timeout
DIGITALOCEAN_IP_POLL_TIMEOUT_MS20000Bounded best-effort public IPv4 poll budget
DIGITALOCEAN_IP_POLL_INTERVAL_MS3000Public IPv4 poll interval
DIGITALOCEAN_ACTION_POLL_TIMEOUT_MS60000Block Storage action completion budget
DIGITALOCEAN_ACTION_POLL_INTERVAL_MS1000Block Storage action poll interval
DIGITALOCEAN_MAX_LIST_PAGES20Maximum pages per DigitalOcean list request
DIGITALOCEAN_REGIONfra1Default DigitalOcean region
DIGITALOCEAN_IMAGEubuntu-24-04-x64Default Droplet image slug
CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS120000Instant-session create-workspace budget (includes in-container clone)
VariableDefaultDescription
OBSERVABILITY_ERROR_RETENTION_DAYS30Error log retention
OBSERVABILITY_ERROR_MAX_ROWS100000Max stored error rows
OBSERVABILITY_ERROR_BATCH_SIZE25Error ingestion batch size
OBSERVABILITY_ERROR_MESSAGE_MAX_LENGTH2048Maximum persisted message length
OBSERVABILITY_ERROR_STACK_MAX_LENGTH4096Maximum persisted stack length
OBSERVABILITY_ERROR_USER_AGENT_MAX_LENGTH512Maximum persisted user-agent length
OBSERVABILITY_LOG_QUERY_RATE_LIMIT30Log queries per minute per admin
VariableDefaultDescription
VM_AGENT_PROTOCOLhttpsProtocol for VM agent communication
VM_AGENT_PORT8443VM agent listening port
ORIGIN_CA_CERT_VALIDITY_DAYS7Validity for per-node Origin CA certificates signed by the API Worker

New nodes generate /etc/sam/tls/origin-ca-key.pem locally in cloud-init and fetch only the signed certificate from POST /api/nodes/:id/origin-ca-certificate (packages/cloud-init/src/template.ts, apps/api/src/routes/node-lifecycle.ts). Legacy ORIGIN_CA_CERT and ORIGIN_CA_KEY Worker secrets are not required for new node provisioning.

Applied via cloud-init on each node:

SettingDefaultDescription
SystemMaxUse500MMax disk space for journal
SystemKeepFree1GMinimum free disk to maintain
MaxRetentionSec7dayMax log retention period
StoragepersistentPersist logs across reboots
CompressyesCompress stored entries
VariableDefaultDescription
FILE_UPLOAD_MAX_BYTES52428800 (50 MB)Max size per uploaded file
FILE_UPLOAD_BATCH_MAX_BYTES262144000 (250 MB)Max total size per upload batch
FILE_UPLOAD_TIMEOUT120sUpload timeout (VM agent)
FILE_UPLOAD_TIMEOUT_MS120000 (120s)Upload proxy timeout (Worker)
FILE_DOWNLOAD_TIMEOUT_MS60000 (60s)Download proxy timeout
FILE_DOWNLOAD_MAX_BYTES52428800 (50 MB)Max download file size
VariableDefaultDescription
FILE_PROXY_TIMEOUT_MS15000File proxy request timeout
FILE_PROXY_MAX_RESPONSE_BYTES2097152 (2 MB)Max file proxy response size
FILE_RAW_MAX_SIZE52428800 (50 MB)Max raw binary file size (VM agent)
FILE_RAW_TIMEOUT60sRaw file streaming timeout (VM agent)
FILE_RAW_PROXY_MAX_BYTES52428800 (50 MB)Max raw file proxy size (Worker)
VariableDefaultDescription
REPO_BROWSE_MAX_INLINE_BYTES1000000 (1 MB)Max bytes to inline as text in the file viewer; larger stream raw
REPO_BROWSE_MAX_COMPARE_FILES300Max changed files in an Artifacts diff before truncation
VariableDefaultDescription
MCP_IDEA_CONTEXT_MAX_LENGTH500Max characters of idea context shown to agents
MCP_IDEA_LIST_LIMIT20Default page size for list_ideas
MCP_IDEA_LIST_MAX100Max page size for list_ideas
MCP_IDEA_SEARCH_MAX20Max results from search_ideas
MCP_MESSAGE_SEARCH_MAX20Max results from search_messages
MCP_MESSAGE_LIST_LIMIT50Default page size for get_session_messages
MCP_MESSAGE_LIST_MAX200Max messages per get_session_messages request
MCP_TRIGGER_LIST_LIMIT20Default page size for list_triggers
MCP_TRIGGER_LIST_MAX100Max triggers per list_triggers request
VariableDefaultDescription
VITE_FILE_PREVIEW_INLINE_MAX_BYTES10485760 (10 MB)Images below this size render inline automatically
VITE_FILE_PREVIEW_LOAD_MAX_BYTES52428800 (50 MB)Images below this size show click-to-load; above shows download link
VITE_ANALYTICS_MAX_QUEUE_SIZE100Max client-side analytics events retained before oldest events drop
VITE_ANALYTICS_FLUSH_THRESHOLD10Client event count that triggers an immediate analytics flush
VITE_ANALYTICS_FLUSH_INTERVAL_MS5000Client analytics background flush interval in milliseconds
VITE_DEBUG_DIAGNOSIS_EVENT_MAX_PAGES100Max paginated diagnosis-event pages loaded per browser request
VITE_PROJECT_LIST_LIMIT50Projects loaded into each shared list-cache entry
VITE_PROJECT_POLL_INTERVAL_MS30000Project-list page refresh cadence in milliseconds; 0 disables
VITE_SIDEBAR_PROJECT_POLL_INTERVAL_MS60000App-shell project-list refresh cadence in milliseconds; 0 disables
VITE_PROJECT_PREFETCH_DELAY_MS120Mouse dwell before project-detail prefetch; focus/touch are immediate
VITE_BACKGROUND_FETCH_DELAY_MS150Delay before background query activity is shown and announced
VITE_CHUNK_LOAD_RETRY_DELAY_MS350Wait before retrying a failed lazy route-chunk import
VITE_CHUNK_RELOAD_COOLDOWN_MS15000Minimum gap between chunk-recovery reloads; guards against a reload loop
VITE_ROUTE_FALLBACK_REVEAL_DELAY_MS180Delay before the route loading spinner fades in, avoiding a flash
VITE_QUERY_PERSIST_MAX_AGE_MS86400000 (24 h)How long a persisted query-cache record may be restored after writing
VITE_QUERY_PERSIST_THROTTLE_MS1000Minimum gap between IndexedDB writes of the query cache
VITE_QUERY_PERSIST_RESTORE_TIMEOUT_MS250Budget for the initial cache restore before failing open to no cache

The control-plane UI writes an allowlisted slice of its query cache to IndexedDB so a full page reload paints from cache instead of refetching. Only bounded project reference data is persisted; chat messages, agent output, credentials, admin diagnostics, node and workspace runtime details, and file contents are never written to disk.

Records are namespaced by authenticated user and by a schema version, and are deleted on sign-out and on account switch, so one account can never be shown another account’s cached data. If IndexedDB is unavailable — private browsing, a storage quota failure, or a disabled store — the app degrades silently to its normal in-memory cache.

SAM uses first-party analytics ingestion for operational/product aggregates. Browser events are batched to /api/t; request analytics are written by API middleware when enabled. Analytics is best-effort and disabled paths preserve normal application behavior.

Client page/referrer fields follow a privacy normalization contract before enqueue: query strings, fragments, protocol, host/userinfo for page values, credentials, emails, UUIDs/ULIDs, long opaque tokens, common secret prefixes, repository/code file identifiers, and values after sensitive route markers are removed or replaced with [redacted]. Non-sensitive nested path shape, event names, durations, UTM source/medium/campaign, session ID, visitor/authenticated user ID, and explicit safe entity metadata are preserved for aggregate reporting.

VariableDefaultDescription
ANALYTICS_ENABLEDtrueEnable API middleware analytics; set false to skip request event writes
ANALYTICS_SKIP_ROUTES(built-in skip list)Comma-separated extra route prefixes/patterns excluded from middleware writes
ANALYTICS_DATASET(deployment-generated)Cloudflare Analytics Engine dataset name
ANALYTICS_SQL_API_URLhttps://api.cloudflare.com/client/v4/accountsAnalytics Engine SQL API base URL override
ANALYTICS_DEFAULT_PERIOD_DAYS30Default admin analytics query lookback in days
ANALYTICS_TOP_EVENTS_LIMIT50Max rows returned by top-events admin query
ANALYTICS_GEO_LIMIT50Max countries in geographic distribution view
ANALYTICS_RETENTION_WEEKS12Number of weeks for retention cohort analysis
ANALYTICS_WEBSITE_TRAFFIC_TOP_PAGES_LIMIT20Max top pages/referrers/events in website traffic sections
ANALYTICS_INGEST_ENABLEDtrueEnable browser event ingestion at /api/t; false returns success without writes
RATE_LIMIT_ANALYTICS_INGEST500Analytics ingest requests allowed per IP per hour
MAX_ANALYTICS_INGEST_BATCH_SIZE25Max browser events accepted per ingest request
MAX_ANALYTICS_INGEST_BODY_BYTES65536Max ingest request body size in bytes
MAX_ANALYTICS_DURATION_MS3600000Max accepted page-duration value; larger values are clamped

External analytics forwarding is off by default. When enabled, SAM forwards only analytics rows already accepted by first-party ingestion/middleware; it does not bypass the client-side URL normalization contract.

VariableDefaultDescription
ANALYTICS_FORWARD_ENABLEDfalseEnable external analytics event forwarding
ANALYTICS_FORWARD_EVENTSkey conversion eventsComma-separated list of events to forward
ANALYTICS_FORWARD_LOOKBACK_HOURS25Hours to look back for events
ANALYTICS_FORWARD_CURSOR_KEYanalytics-forward-cursorKV key used to remember forwarded progress
ANALYTICS_FORWARD_SQL_LIMIT10000Max rows fetched per forwarding run
ANALYTICS_SQL_FETCH_TIMEOUT_MS30000Timeout for Analytics Engine SQL fetches
SEGMENT_WRITE_KEY(unset)Segment Write Key for event forwarding
SEGMENT_API_URLhttps://api.segment.io/v1/batchSegment API endpoint
SEGMENT_MAX_BATCH_SIZE100Max events per Segment batch request
GA4_MEASUREMENT_ID(unset)Google Analytics 4 Measurement ID
GA4_API_SECRET(unset)Google Analytics 4 API secret
GA4_API_URLhttps://www.google-analytics.com/mp/collectGA4 Measurement Protocol endpoint
GA4_MAX_BATCH_SIZE25Max events per GA4 batch request