Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ Create an R2 API Token:
1. Go to [Modal Settings](https://modal.com/settings)
2. **Create a new API token**: Settings -> API Tokens -> New Token
3. Note the **Token ID** and **Token Secret**
4. Note your **Workspace name** (visible in your Modal dashboard URL)
4. Note your **Workspace** and **Environment names** (visible in your Modal dashboard URL,
https://modal.com/apps/<modal_workspace>/<modal_environment>)

### Daytona

Expand Down Expand Up @@ -355,6 +356,7 @@ vercel_team_id = "team_xxxxx" # Your Vercel ID (even personal
modal_token_id = "your-modal-token-id"
modal_token_secret = "your-modal-token-secret"
modal_workspace = "your-modal-workspace"
modal_environment = "your-modal-environment"

# Daytona (only required when sandbox_provider = "daytona")
# daytona_api_url = "https://app.daytona.io/api"
Expand Down
4 changes: 4 additions & 0 deletions terraform/environments/production/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ locals {
name_suffix = var.deployment_name
use_modal_backend = var.sandbox_provider == "modal"
use_daytona_backend = var.sandbox_provider == "daytona"
# Modal omits the environment segment for the default "main" environment.
# Non-main environments: "{workspace}-{env}--{app}.modal.run"
# Default (main) environment: "{workspace}--{app}.modal.run"
modal_workspace_slug = var.modal_environment == "main" ? var.modal_workspace : "${var.modal_workspace}-${var.modal_environment}"

# URLs for cross-service configuration
control_plane_host = "open-inspect-control-plane-${local.name_suffix}.${var.cloudflare_worker_subdomain}.workers.dev"
Expand Down
2 changes: 1 addition & 1 deletion terraform/environments/production/modal.tf
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module "modal_app" {
modal_token_secret = var.modal_token_secret

app_name = "open-inspect"
workspace = var.modal_workspace
workspace = local.modal_workspace_slug
Copy link
Copy Markdown
Owner

@ColeMurray ColeMurray May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Automated Review]
The slug is plumbed into URL construction here, but modal deploy / modal secret create in terraform/modules/modal-app/scripts/{deploy,create-secrets}.sh are invoked without --env (and no MODAL_ENVIRONMENT is exported), so they deploy to the workspace's default environment regardless of var.modal_environment.

Concretely, with modal_environment = "production":

  • This module's outputs and the worker's MODAL_WORKSPACEmyworkspace-production--…modal.run
  • But modal deploy lands the app at the workspace default (typically main) → myworkspace--…modal.run
  • Result: every endpoint 404s.

Suggested fix:

  1. Add a modal_environment input to the modal-app module.
  2. Pass it as MODAL_ENVIRONMENT in the environment blocks of null_resource.modal_secrets and null_resource.modal_deploy in modules/modal-app/main.tf — Modal CLI reads that var natively.
    (Or alternatively, append --env "${MODAL_ENVIRONMENT}" in both scripts.)

Without this, the feature is half-wired for non-main envs — URLs point one place, deployment lands somewhere else.

deploy_path = "${var.project_root}/packages/modal-infra"
deploy_module = "deploy"
source_hash = data.external.modal_source_hash[0].result.hash
Expand Down
10 changes: 7 additions & 3 deletions terraform/environments/production/terraform.tfvars.example
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ cloudflare_worker_subdomain = ""
modal_token_id = ""
modal_token_secret = ""

# Modal workspace name (used in endpoint URLs)
# Modal workspace name and environment (used in endpoint URLs)
# Only required when sandbox_provider = "modal"
# This is your Modal username/workspace (e.g., "myworkspace" in "https://myworkspace--app.modal.run")
modal_workspace = ""
# workspace: your Modal username (e.g., "myworkspace")
# environment: Modal environment name. Use "main" for the default environment:
# main env: https://myworkspace--app.modal.run
# non-main env: https://myworkspace-production--app.modal.run
modal_workspace = ""
modal_environment = "main"

# Daytona REST API
# Only required when sandbox_provider = "daytona"
Expand Down
11 changes: 11 additions & 0 deletions terraform/environments/production/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ variable "modal_workspace" {
}
}

variable "modal_environment" {
description = "Modal environment name. Use 'main' for the default environment (omitted from endpoint URLs). Non-main envs (e.g., 'production', 'dev') are included in the URL slug."
type = string
default = "main"

validation {
condition = var.sandbox_provider != "modal" || length(var.modal_environment) > 0
error_message = "modal_environment must be set when sandbox_provider = 'modal'."
}
}
Comment thread
ColeMurray marked this conversation as resolved.

# =============================================================================
# GitHub OAuth App Credentials
# =============================================================================
Expand Down
2 changes: 1 addition & 1 deletion terraform/environments/production/workers-control-plane.tf
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ module "control_plane_worker" {
{ name = "APP_NAME", value = var.app_name },
{ name = "SANDBOX_PROVIDER", value = var.sandbox_provider },
],
local.use_modal_backend ? [{ name = "MODAL_WORKSPACE", value = var.modal_workspace }] : [],
local.use_modal_backend ? [{ name = "MODAL_WORKSPACE", value = local.modal_workspace_slug }] : [],
local.use_daytona_backend ? [
{ name = "DAYTONA_API_URL", value = var.daytona_api_url },
{ name = "DAYTONA_BASE_SNAPSHOT", value = var.daytona_base_snapshot },
Expand Down
Loading