Skip to content

feat(terraform): support custom domains for Cloudflare workers#604

Open
donnfelker wants to merge 6 commits into
ColeMurray:mainfrom
donnfelker:upstream-custom-domains
Open

feat(terraform): support custom domains for Cloudflare workers#604
donnfelker wants to merge 6 commits into
ColeMurray:mainfrom
donnfelker:upstream-custom-domains

Conversation

@donnfelker
Copy link
Copy Markdown
Contributor

@donnfelker donnfelker commented May 8, 2026

Summary

  • Adds two optional Terraform variables — web_app_domain and control_plane_domain — that override the default *.workers.dev URLs in cross-service config (NEXTAUTH_URL, NEXT_PUBLIC_WS_URL, CONTROL_PLANE_URL, Modal allowed hosts) and outputs.
  • Cloudflare-only feature. web_app_domain takes effect only when web_platform = "cloudflare" — Vercel deployments use Vercel-managed domains and ignore the variable. control_plane_domain always applies since the control plane is on Cloudflare regardless of web platform.
  • Both default to empty, so existing deployments produce no diff. The variables describe the URL/identity layer only — binding the hostname to the worker (Cloudflare dashboard or cloudflare_workers_custom_domain) is out of scope and left to the operator.
  • Documents the full cutover in docs/GETTING_STARTED.md Step 9b (binding, GitHub App callback update, apply, smoke-test, troubleshooting table) plus a pointer in docs/SETUP_GUIDE.md. Both are flagged "Cloudflare only".
  • Adds terraform test coverage with five plan-mode runs (including vercel_platform_ignores_web_app_domain to lock the no-op behavior on the Vercel path) and wires terraform test into the existing validate CI job.

Summary by CodeRabbit

  • New Features

    • Added custom domain support for Cloudflare deployments (web app and control plane hostnames)
  • Tests

    • Added Terraform plan-mode tests for custom-domain behavior
    • CI now runs Terraform tests during pre-plan validation and reports test results in the PR summary
  • Documentation

    • Updated setup/getting-started docs with custom-domain setup, examples, callback updates, smoke-test steps, and troubleshooting guidance

Review Change Stack

donnfelker and others added 5 commits May 8, 2026 14:35
…trol plane

Adds two optional input variables — web_app_domain and control_plane_domain —
that override the default workers.dev URLs in cross-service configuration
(NEXTAUTH_URL, NEXT_PUBLIC_WS_URL, CONTROL_PLANE_URL, Modal allowed hosts)
and in outputs. Both default to empty so existing deployments produce no diff.

The variables describe the URL/identity layer only. Binding the hostname to
the Cloudflare worker (cloudflare_workers_custom_domain or dashboard) is
out of scope and left to the operator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds Step 9b to GETTING_STARTED.md walking through binding a hostname to
the Cloudflare worker, setting web_app_domain / control_plane_domain in
terraform.tfvars, updating the GitHub App callback, and smoke-testing.
Includes a troubleshooting table for the common cutover failure modes
(NEXTAUTH_URL mismatch, GitHub callback typo, WSS failure, 522/525 on the
new domain). Updates the example tfvars block in the same step to include
the two new variables.

Adds a brief pointer in SETUP_GUIDE.md's Path C section so contributors
deploying their own stack are aware of the option without duplicating the
full procedure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds tests/custom_domains.tftest.hcl with five plan-mode runs that exercise
the override and fallback logic in locals.tf:

- defaults_use_workers_dev: empty vars resolve to workers.dev fallbacks
- web_app_domain_overrides_only_web: web override leaves control plane alone
- control_plane_domain_overrides_only_control_plane: inverse case, also
  asserts control_plane_url and ws_url propagation
- both_domains_set: both overrides applied
- vercel_platform_ignores_web_app_domain: web_app_domain has no effect
  when web_platform = vercel

Tests use mock_provider blocks for cloudflare and vercel, so they require
no real credentials and create no resources. They run in seconds.

Wires `terraform test -no-color` into the existing validate job in
.github/workflows/terraform.yml and surfaces the result in the PR comment
status table.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…n is set

Adds a validation block to web_app_domain that fails at plan time if
web_platform is anything other than "cloudflare". Vercel deployments use
Vercel-managed domains and ignored web_app_domain silently — that silent
no-op was the kind of thing that bites you on a redeploy.

Replaces the existing "vercel_platform_ignores_web_app_domain" tftest run
with two cases:

- vercel_platform_rejects_web_app_domain — uses expect_failures to assert
  the validation fires when both are set
- vercel_platform_with_empty_web_app_domain_is_allowed — empty value still
  works on Vercel, so the validation isn't overzealous

Cross-variable validation is supported on Terraform >= 1.9; the project
already requires >= 1.14.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates the variables.tf comment block, variable descriptions,
terraform.tfvars.example, GETTING_STARTED.md Step 9b, and SETUP_GUIDE.md
Path C to be explicit about three things:

1. The feature is Cloudflare-only. web_app_domain only takes effect when
   web_platform = "cloudflare" (now rejected at plan time when set with
   web_platform = "vercel"). control_plane_domain always applies because
   the control plane is a Cloudflare Worker regardless of web platform.

2. Modal vs Daytona behavior. When sandbox_provider = "modal",
   control_plane_domain flows into Modal's ALLOWED_CONTROL_PLANE_HOSTS
   automatically. Daytona has no analogue and needs no wiring — the
   control plane calls Daytona's REST API, not the reverse, so there's
   no host allowlist to maintain.

3. Variable descriptions tightened to encode the platform/sandbox
   constraints inline so they show up in `terraform console` and IDE
   hover tooltips, not just in the comment block.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f2332e33-41c2-4bcd-99bf-e02e49153589

📥 Commits

Reviewing files that changed from the base of the PR and between 5ddac92 and 452f090.

📒 Files selected for processing (1)
  • docs/GETTING_STARTED.md
✅ Files skipped from review due to trivial changes (1)
  • docs/GETTING_STARTED.md

📝 Walkthrough

Walkthrough

Adds optional Cloudflare custom-domain variables, resolves derived URLs/hosts with workers.dev fallbacks, updates outputs and verification commands, adds Terraform tests and CI reporting for tests, and documents setup and troubleshooting.

Changes

Cloudflare Custom Domain Support

Layer / File(s) Summary
Variable Definitions
terraform/environments/production/variables.tf
Added web_app_domain (Cloudflare-only with validation) and control_plane_domain (always-applicable) variables with explanatory comments about interactions with web_platform and sandbox_provider.
Locals & URL Derivation
terraform/environments/production/locals.tf
Introduced default_control_plane_host and default_web_app_host, and redefined control_plane_host, control_plane_url, ws_url, and Cloudflare web_app_url to prefer provided domains with workers.dev fallback.
Outputs & Verification
terraform/environments/production/outputs.tf
Updated output.control_plane_url and verification_commands to reference local.control_plane_url so health/session curl targets use the derived URL.
Terraform Test Suite
terraform/environments/production/tests/custom_domains.tftest.hcl
Added test suite with runs: defaults fallback, web-only override, control-plane-only override, both overrides, Vercel rejection (non-empty web_app_domain), and Vercel allow (empty web_app_domain).
CI Integration & Reporting
.github/workflows/terraform.yml
Added a Terraform Test step to the validate job and updated the PR validation comment to include the test step outcome.
Documentation & Examples
terraform/environments/production/terraform.tfvars.example, docs/GETTING_STARTED.md, docs/SETUP_GUIDE.md
Updated example tfvars with web_app_domain/control_plane_domain, added Step 9b walkthrough in GETTING_STARTED.md for binding, tfvars, callback updates, smoke tests, and troubleshooting; added Cloudflare custom domains subsection in SETUP_GUIDE.md.

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant GH as "GitHub Actions"
  participant TF as "Terraform (validate job)"
  participant Cloudflare
  Operator->>GH: push PR / set tfvars
  GH->>TF: run format, init, validate, test
  TF->>Cloudflare: resolve/derive host URLs (using locals/vars)
  TF->>GH: report outcomes (format, init, validate, test)
  GH->>Operator: post PR comment with outcomes
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • ColeMurray
  • open-inspect

Poem

🐰 I hopped through hosts both old and new,
I found where workers.dev once grew.
Now custom domains can take their place,
Tests confirm each mapped-out space,
Docs show paths — hop on, review! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding support for custom domains (web_app_domain and control_plane_domain) for Cloudflare workers in Terraform configuration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
terraform/environments/production/variables.tf (1)

449-464: ⚡ Quick win

Add format guard to reject scheme-prefixed domain inputs.

Neither web_app_domain nor control_plane_domain validates that the value is a bare hostname. A user who supplies "https://app.example.com" instead of "app.example.com" will silently get broken URLs (https://https://app.example.com) baked into the worker bundles with no plan-time feedback.

🛡️ Proposed validation additions
 variable "web_app_domain" {
   description = "Custom hostname for the Cloudflare web Worker. Drives NEXTAUTH_URL. Has no effect when web_platform = \"vercel\" (rejected at plan time when both are set)."
   type        = string
   default     = ""

   validation {
     condition     = var.web_app_domain == "" || var.web_platform == "cloudflare"
     error_message = "web_app_domain only takes effect when web_platform = \"cloudflare\". Vercel deployments use Vercel-managed domains and ignore this variable. Either remove web_app_domain or switch web_platform to \"cloudflare\"."
   }
+
+  validation {
+    condition     = var.web_app_domain == "" || !startswith(var.web_app_domain, "http")
+    error_message = "web_app_domain must be a bare hostname (e.g. \"app.example.com\"), not a URL. Remove the scheme prefix."
+  }
 }

 variable "control_plane_domain" {
   description = "Custom hostname for the Cloudflare control plane Worker. Drives CONTROL_PLANE_URL, NEXT_PUBLIC_WS_URL, and (when sandbox_provider = \"modal\") Modal's ALLOWED_CONTROL_PLANE_HOSTS. No-op for Daytona's REST-pull model."
   type        = string
   default     = ""
+
+  validation {
+    condition     = var.control_plane_domain == "" || !startswith(var.control_plane_domain, "http")
+    error_message = "control_plane_domain must be a bare hostname (e.g. \"api.example.com\"), not a URL. Remove the scheme prefix."
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@terraform/environments/production/variables.tf` around lines 449 - 464, Add
validation to ensure web_app_domain and control_plane_domain are bare hostnames
by rejecting values containing a URL scheme; update the existing validation
block for variable "web_app_domain" (inside its validation.condition) to also
require that var.web_app_domain does not contain "://", and add a new validation
block for variable "control_plane_domain" that requires var.control_plane_domain
== "" || !contains(var.control_plane_domain, "://") with an error_message
telling users to provide a bare hostname (e.g. "app.example.com") rather than a
scheme-prefixed value like "https://app.example.com".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/GETTING_STARTED.md`:
- Around line 710-712: Add a language identifier to the fenced code block that
currently contains the single-line URL
("https://app.example.com/api/auth/callback/github") to satisfy MD040 and enable
proper rendering; update the fenced block around that URL to use a plaintext
language tag (e.g., "text") so the block becomes ```text ... ``` while leaving
the URL content unchanged.

---

Nitpick comments:
In `@terraform/environments/production/variables.tf`:
- Around line 449-464: Add validation to ensure web_app_domain and
control_plane_domain are bare hostnames by rejecting values containing a URL
scheme; update the existing validation block for variable "web_app_domain"
(inside its validation.condition) to also require that var.web_app_domain does
not contain "://", and add a new validation block for variable
"control_plane_domain" that requires var.control_plane_domain == "" ||
!contains(var.control_plane_domain, "://") with an error_message telling users
to provide a bare hostname (e.g. "app.example.com") rather than a
scheme-prefixed value like "https://app.example.com".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d81a347-63d6-4548-b7cf-0a8cfbc90968

📥 Commits

Reviewing files that changed from the base of the PR and between 74a4fa1 and 5ddac92.

📒 Files selected for processing (8)
  • .github/workflows/terraform.yml
  • docs/GETTING_STARTED.md
  • docs/SETUP_GUIDE.md
  • terraform/environments/production/locals.tf
  • terraform/environments/production/outputs.tf
  • terraform/environments/production/terraform.tfvars.example
  • terraform/environments/production/tests/custom_domains.tftest.hcl
  • terraform/environments/production/variables.tf

Comment thread docs/GETTING_STARTED.md
Satisfies markdownlint MD040 and ensures consistent rendering.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@ColeMurray ColeMurray left a comment

Choose a reason for hiding this comment

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

[Automated Review] Requesting changes for the custom-domain deployment issues noted inline.

#
# Set the hostname only (no scheme): "app.example.com", not "https://...".

variable "web_app_domain" {
Copy link
Copy Markdown
Owner

@ColeMurray ColeMurray May 14, 2026

Choose a reason for hiding this comment

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

[Automated Review] For deployments that rely on the provided GitHub Actions Terraform plan/apply path, terraform.tfvars is ignored and variables are supplied as TF_VAR_* secrets in the workflow. These new variables default to empty, but the workflow never exports TF_VAR_web_app_domain or TF_VAR_control_plane_domain, so CI-driven applies keep publishing the workers.dev URLs even after operators configure custom-domain secrets.

Concretely, please add the two exports to both workflow env blocks:

TF_VAR_web_app_domain: ${{ secrets.WEB_APP_DOMAIN }}
TF_VAR_control_plane_domain: ${{ secrets.CONTROL_PLANE_DOMAIN }}

Places to update:

  • Terraform Plan env block in .github/workflows/terraform.yml around lines 188-230:
    env:
    TF_VAR_cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    TF_VAR_cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    TF_VAR_cloudflare_worker_subdomain: ${{ secrets.CLOUDFLARE_WORKER_SUBDOMAIN }}
    TF_VAR_vercel_api_token: "${{ secrets.VERCEL_API_TOKEN || 'unused' }}"
    TF_VAR_vercel_team_id: "${{ secrets.VERCEL_TEAM_ID || 'unused' }}"
    TF_VAR_modal_token_id: ${{ secrets.MODAL_TOKEN_ID }}
    TF_VAR_modal_token_secret: ${{ secrets.MODAL_TOKEN_SECRET }}
    TF_VAR_modal_workspace: ${{ secrets.MODAL_WORKSPACE }}
    TF_VAR_github_client_id: ${{ secrets.GH_OAUTH_CLIENT_ID }}
    TF_VAR_github_client_secret: ${{ secrets.GH_OAUTH_CLIENT_SECRET }}
    TF_VAR_github_app_id: ${{ secrets.GH_APP_ID }}
    TF_VAR_github_app_private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
    TF_VAR_github_app_installation_id: ${{ secrets.GH_APP_INSTALLATION_ID }}
    TF_VAR_enable_slack_bot: "${{ secrets.ENABLE_SLACK_BOT || 'true' }}"
    TF_VAR_slack_bot_token: ${{ secrets.SLACK_BOT_TOKEN }}
    TF_VAR_slack_signing_secret: ${{ secrets.SLACK_SIGNING_SECRET }}
    TF_VAR_anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
    TF_VAR_token_encryption_key: ${{ secrets.TOKEN_ENCRYPTION_KEY }}
    TF_VAR_repo_secrets_encryption_key: ${{ secrets.REPO_SECRETS_ENCRYPTION_KEY }}
    TF_VAR_internal_callback_secret: ${{ secrets.INTERNAL_CALLBACK_SECRET }}
    TF_VAR_nextauth_secret: ${{ secrets.NEXTAUTH_SECRET }}
    TF_VAR_modal_api_secret: ${{ secrets.MODAL_API_SECRET }}
    TF_VAR_allowed_users: ${{ secrets.ALLOWED_USERS }}
    TF_VAR_allowed_email_domains: ${{ secrets.ALLOWED_EMAIL_DOMAINS }}
    TF_VAR_deployment_name: ${{ secrets.DEPLOYMENT_NAME }}
    TF_VAR_enable_github_bot: "${{ secrets.ENABLE_GITHUB_BOT || 'false' }}"
    TF_VAR_github_webhook_secret: ${{ secrets.GH_WEBHOOK_SECRET }}
    TF_VAR_github_bot_username: ${{ secrets.GH_BOT_USERNAME }}
    TF_VAR_app_name: "${{ secrets.APP_NAME || 'Open-Inspect' }}"
    TF_VAR_app_short_name: ${{ secrets.APP_SHORT_NAME }}
    TF_VAR_app_icon_url: ${{ secrets.APP_ICON_URL }}
    TF_VAR_enable_linear_bot: "${{ secrets.ENABLE_LINEAR_BOT || 'false' }}"
    TF_VAR_linear_client_id: ${{ secrets.LINEAR_CLIENT_ID }}
    TF_VAR_linear_client_secret: ${{ secrets.LINEAR_CLIENT_SECRET }}
    TF_VAR_linear_webhook_secret: ${{ secrets.LINEAR_WEBHOOK_SECRET }}
    TF_VAR_web_platform: "${{ secrets.WEB_PLATFORM || 'vercel' }}"
    TF_VAR_enable_durable_object_bindings: "${{ secrets.ENABLE_DURABLE_OBJECT_BINDINGS || 'true' }}"
    TF_VAR_sandbox_provider: "${{ secrets.SANDBOX_PROVIDER || 'modal' }}"
    TF_VAR_daytona_api_url: ${{ secrets.DAYTONA_API_URL }}
    TF_VAR_daytona_api_key: ${{ secrets.DAYTONA_API_KEY }}
    TF_VAR_daytona_base_snapshot: ${{ secrets.DAYTONA_BASE_SNAPSHOT }}
    TF_VAR_daytona_target: ${{ secrets.DAYTONA_TARGET }}
  • Terraform Apply env block in .github/workflows/terraform.yml around lines 319-361:
    env:
    TF_VAR_cloudflare_api_token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
    TF_VAR_cloudflare_account_id: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    TF_VAR_cloudflare_worker_subdomain: ${{ secrets.CLOUDFLARE_WORKER_SUBDOMAIN }}
    TF_VAR_vercel_api_token: "${{ secrets.VERCEL_API_TOKEN || 'unused' }}"
    TF_VAR_vercel_team_id: "${{ secrets.VERCEL_TEAM_ID || 'unused' }}"
    TF_VAR_modal_token_id: ${{ secrets.MODAL_TOKEN_ID }}
    TF_VAR_modal_token_secret: ${{ secrets.MODAL_TOKEN_SECRET }}
    TF_VAR_modal_workspace: ${{ secrets.MODAL_WORKSPACE }}
    TF_VAR_github_client_id: ${{ secrets.GH_OAUTH_CLIENT_ID }}
    TF_VAR_github_client_secret: ${{ secrets.GH_OAUTH_CLIENT_SECRET }}
    TF_VAR_github_app_id: ${{ secrets.GH_APP_ID }}
    TF_VAR_github_app_private_key: ${{ secrets.GH_APP_PRIVATE_KEY }}
    TF_VAR_github_app_installation_id: ${{ secrets.GH_APP_INSTALLATION_ID }}
    TF_VAR_enable_slack_bot: "${{ secrets.ENABLE_SLACK_BOT || 'true' }}"
    TF_VAR_slack_bot_token: ${{ secrets.SLACK_BOT_TOKEN }}
    TF_VAR_slack_signing_secret: ${{ secrets.SLACK_SIGNING_SECRET }}
    TF_VAR_anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
    TF_VAR_token_encryption_key: ${{ secrets.TOKEN_ENCRYPTION_KEY }}
    TF_VAR_repo_secrets_encryption_key: ${{ secrets.REPO_SECRETS_ENCRYPTION_KEY }}
    TF_VAR_internal_callback_secret: ${{ secrets.INTERNAL_CALLBACK_SECRET }}
    TF_VAR_nextauth_secret: ${{ secrets.NEXTAUTH_SECRET }}
    TF_VAR_modal_api_secret: ${{ secrets.MODAL_API_SECRET }}
    TF_VAR_allowed_users: ${{ secrets.ALLOWED_USERS }}
    TF_VAR_allowed_email_domains: ${{ secrets.ALLOWED_EMAIL_DOMAINS }}
    TF_VAR_deployment_name: ${{ secrets.DEPLOYMENT_NAME }}
    TF_VAR_enable_github_bot: "${{ secrets.ENABLE_GITHUB_BOT || 'false' }}"
    TF_VAR_github_webhook_secret: ${{ secrets.GH_WEBHOOK_SECRET }}
    TF_VAR_github_bot_username: ${{ secrets.GH_BOT_USERNAME }}
    TF_VAR_app_name: "${{ secrets.APP_NAME || 'Open-Inspect' }}"
    TF_VAR_app_short_name: ${{ secrets.APP_SHORT_NAME }}
    TF_VAR_app_icon_url: ${{ secrets.APP_ICON_URL }}
    TF_VAR_enable_linear_bot: "${{ secrets.ENABLE_LINEAR_BOT || 'false' }}"
    TF_VAR_linear_client_id: ${{ secrets.LINEAR_CLIENT_ID }}
    TF_VAR_linear_client_secret: ${{ secrets.LINEAR_CLIENT_SECRET }}
    TF_VAR_linear_webhook_secret: ${{ secrets.LINEAR_WEBHOOK_SECRET }}
    TF_VAR_web_platform: "${{ secrets.WEB_PLATFORM || 'vercel' }}"
    TF_VAR_enable_durable_object_bindings: "${{ secrets.ENABLE_DURABLE_OBJECT_BINDINGS || 'true' }}"
    TF_VAR_sandbox_provider: "${{ secrets.SANDBOX_PROVIDER || 'modal' }}"
    TF_VAR_daytona_api_url: ${{ secrets.DAYTONA_API_URL }}
    TF_VAR_daytona_api_key: ${{ secrets.DAYTONA_API_KEY }}
    TF_VAR_daytona_base_snapshot: ${{ secrets.DAYTONA_BASE_SNAPSHOT }}
    TF_VAR_daytona_target: ${{ secrets.DAYTONA_TARGET }}
  • CI/CD secrets table in docs/GETTING_STARTED.md around lines 760-800, documenting WEB_APP_DOMAIN and CONTROL_PLANE_DOMAIN:
    Go to your fork's Settings → Secrets and variables → Actions, and add:
    | Secret Name | Value |
    | ----------------------------- | ----------------------------------------------------------------------------- |
    | `CLOUDFLARE_API_TOKEN` | Your Cloudflare API token |
    | `CLOUDFLARE_ACCOUNT_ID` | Your Cloudflare account ID |
    | `CLOUDFLARE_WORKER_SUBDOMAIN` | Your workers.dev subdomain |
    | `DEPLOYMENT_NAME` | Your deployment name |
    | `R2_ACCESS_KEY_ID` | R2 access key ID |
    | `R2_SECRET_ACCESS_KEY` | R2 secret access key |
    | `WEB_PLATFORM` | `vercel` or `cloudflare` |
    | `VERCEL_API_TOKEN` | Vercel API token _(only if `web_platform = "vercel"`)_ |
    | `VERCEL_TEAM_ID` | Vercel team/account ID _(only if `web_platform = "vercel"`)_ |
    | `VERCEL_PROJECT_ID` | Vercel project ID _(only if `web_platform = "vercel"`)_ |
    | `NEXTAUTH_URL` | Your web app URL |
    | `MODAL_TOKEN_ID` | Modal token ID |
    | `MODAL_TOKEN_SECRET` | Modal token secret |
    | `MODAL_WORKSPACE` | Modal workspace name |
    | `GH_OAUTH_CLIENT_ID` | GitHub App OAuth client ID |
    | `GH_OAUTH_CLIENT_SECRET` | GitHub App OAuth client secret |
    | `GH_APP_ID` | GitHub App ID |
    | `GH_APP_PRIVATE_KEY` | GitHub App private key (PKCS#8 format) |
    | `GH_APP_INSTALLATION_ID` | GitHub App installation ID |
    | `ENABLE_SLACK_BOT` | `true` to deploy Slack bot, `false` to skip (default: `true`) |
    | `SLACK_BOT_TOKEN` | Slack bot token (required if enabled) |
    | `SLACK_SIGNING_SECRET` | Slack signing secret (required if enabled) |
    | `ANTHROPIC_API_KEY` | Anthropic API key |
    | `TOKEN_ENCRYPTION_KEY` | Generated encryption key (OAuth tokens) |
    | `REPO_SECRETS_ENCRYPTION_KEY` | Generated encryption key (repo secrets) |
    | `INTERNAL_CALLBACK_SECRET` | Generated callback secret |
    | `MODAL_API_SECRET` | Generated Modal API secret |
    | `NEXTAUTH_SECRET` | Generated NextAuth secret |
    | `ALLOWED_USERS` | Comma-separated GitHub usernames (or empty for all users) |
    | `ALLOWED_EMAIL_DOMAINS` | Comma-separated email domains (or empty for all domains) |
    | `ENABLE_GITHUB_BOT` | `true` to deploy GitHub bot worker (or empty to skip) |
    | `GH_WEBHOOK_SECRET` | GitHub webhook secret (required if GitHub bot enabled) |
    | `GH_BOT_USERNAME` | GitHub App bot username, e.g., `my-app[bot]` (required if GitHub bot enabled) |
    | `APP_NAME` | Optional display name for whitelabeling (default: `Open-Inspect`) |
    | `APP_SHORT_NAME` | Optional short label for sidebar header (default: `Inspect`) |
    | `APP_ICON_URL` | Optional URL to a custom logo/favicon (default: built-in icon) |

Comment on lines +12 to +18
control_plane_host = var.control_plane_domain != "" ? var.control_plane_domain : local.default_control_plane_host
control_plane_url = "https://${local.control_plane_host}"
ws_url = "wss://${local.control_plane_host}"

# Web app URL depends on deployment platform
# Web app URL: custom domain on Cloudflare path, or workers.dev / Vercel default
web_app_url = var.web_platform == "cloudflare" ? (
"https://open-inspect-web-${local.name_suffix}.${var.cloudflare_worker_subdomain}.workers.dev"
var.web_app_domain != "" ? "https://${var.web_app_domain}" : "https://${local.default_web_app_host}"
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

[Automated Review] When an operator provides a common URL-form value such as https://app.example.com for either custom domain, these locals prefix another scheme and bake values like https://https://... or wss://https://... into NextAuth, WebSocket config, and Modal allowlists. Since the variables require bare hostnames, please add plan-time validation rejecting scheme/path input so the deployment fails clearly instead of generating broken URLs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants