| name | onboarding |
| description | Deploy your own Open-Inspect instance. Use when the user wants to set up, deploy, or onboard to Open-Inspect. Guides through repository setup, credential collection, Terraform deployment, and verification with user handoffs. |
| user-invocable | true |
| allowed-tools | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, TodoWrite |
Open-Inspect Deployment Guide
You are guiding the user through deploying their own instance of Open-Inspect. This is a multi-phase
process requiring user interaction for credential collection and external service configuration.
Before Starting
Use TodoWrite to create a checklist tracking these phases:
- Initial setup questions
- Repository setup
- Credential collection (Cloudflare, Vercel, Modal, Anthropic)
- GitHub App creation (+ Google OAuth if enabled)
- Slack App creation (if enabled)
- Security secrets generation
- Terraform configuration
- Terraform deployment (two phases)
- Post-deployment Slack setup (if enabled)
- Post-deployment GitHub Bot setup (if enabled)
- Web app deployment
- Verification
- CI/CD setup (optional)
Phase 1: Initial Questions
First, generate a random suffix suggestion for the user:
echo "Suggested deployment name: $(openssl rand -hex 3)"
Use AskUserQuestion to gather:
- Directory location - Where to create the project (default: current directory or
~/workplace/open-inspect-{suffix})
- GitHub account - Which account/org hosts the private repo
- Deployment name - A globally unique identifier for URLs (e.g., their GitHub username, company
name, or the random suffix generated above). Explain this creates URLs like
open-inspect-{deployment_name}.vercel.app and must be unique across all Vercel users.
- Slack integration - Yes or No
- GitHub bot integration - Yes or No (automated PR reviews and comment-triggered actions)
- Sign-in providers - GitHub, Google, or both. At least one is required.
- Prerequisites confirmation - Confirm they have accounts on Cloudflare, Vercel, Modal,
Anthropic
Phase 2: Repository Setup
Execute these commands (substitute values from Phase 1):
mkdir -p {directory_path}
gh repo create {github_account}/open-inspect-{name} --private --description "Open-Inspect deployment"
cd {directory_path}
git clone git@github.com:ColeMurray/open-inspect.git .
git remote rename origin upstream
git remote add origin git@github.com:{github_account}/open-inspect-{name}.git
git push -u origin main
npm install
npm run build -w @open-inspect/shared
Phase 3: Credential Collection
Hand off to user for each service. Use AskUserQuestion to collect credentials.
Cloudflare
Tell the user:
- Account ID: Found in dashboard URL or account overview
- Workers Subdomain: Workers & Pages → Overview, bottom-right panel shows
*.YOUR-SUBDOMAIN.workers.dev
- API Token: Create at https://dash.cloudflare.com/profile/api-tokens with template "Edit
Cloudflare Workers" + permissions for Workers KV Storage (Edit), Workers R2 Storage (Edit), D1
(Edit)
R2 Bucket
Check wrangler login status, then create bucket:
wrangler whoami
wrangler r2 bucket create open-inspect-{name}-tf-state
Tell user to create R2 API Token at R2 → Overview → Manage R2 API Tokens with "Object Read & Write"
permission.
Vercel
Modal
Then set the token:
modal token set --token-id {token_id} --token-secret {token_secret}
modal profile current
Anthropic
Phase 4: GitHub App Setup
Guide the user through creating a GitHub App. Its App ID, private key, and installation ID are
always required for repository access. Its client ID and secret enable GitHub sign-in only when the
user selected GitHub:
- Go to https://github.com/settings/apps → "New GitHub App"
- Name:
Open-Inspect-{YourName} (globally unique)
- Homepage URL: The deployed web app URL for the selected platform:
- Vercel:
https://open-inspect-{deployment_name}.vercel.app
- Cloudflare workers.dev:
https://open-inspect-web-{deployment_name}.{subdomain}.workers.dev
- Cloudflare custom domain:
https://{your-custom-domain}
- Webhook: Uncheck "Active"
- If GitHub sign-in is selected, set the Callback URL (under "Identifying and authorizing
users"):
{deployed-web-app-url}/api/auth/callback/github
- CRITICAL: The origin must exactly match the Homepage URL selected above.
- Repository permissions: Contents (Read & Write), Pull requests (Read & Write), Metadata
(Read-only), and Issues (Read & Write) only if the GitHub bot is enabled. Pull requests
permission also authorizes creating and applying labels to session-created pull requests;
labeling does not require Issues permission.
- If GitHub sign-in uses email/domain admission, set Account permissions: Email addresses
(Read-only)
- Create app, note App ID
- If GitHub sign-in is selected, generate a Client Secret and note the Client ID and
Client Secret. Otherwise leave both Terraform values empty.
- Generate Private Key (downloads .pem file)
- Install app on account, note Installation ID from URL
After receiving the .pem path, convert to PKCS#8:
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in {pem_path} -out /tmp/github-app-key-pkcs8.pem
cat /tmp/github-app-key-pkcs8.pem
Phase 4b: Google OAuth Setup (If Enabled)
Only if the user selected Google sign-in. Skip for GitHub-only deployments and leave
google_client_id and google_client_secret empty.
Guide user:
- https://console.cloud.google.com/apis/credentials → "Create Credentials" → "OAuth client ID"
- Application type: Web application
- Authorized redirect URI:
https://open-inspect-{deployment_name}.vercel.app/api/auth/callback/google (or your
*.workers.dev web URL if web_platform = "cloudflare")
- CRITICAL: Must match deployed web URL exactly!
- OAuth consent screen: request only
openid, email, profile scopes (non-sensitive — no Google
verification review required)
- Note Client ID and Client Secret
Then in terraform.tfvars:
- Set
google_client_id and google_client_secret (both required together; leave both empty to
disable)
- Add at least one entry to
allowed_emails (exact addresses, e.g. pm@gmail.com) or
allowed_email_domains. Prefer allowed_emails for shared domains like gmail.com.
- If Google is the only sign-in provider, leave
github_client_id and github_client_secret empty.
Keep the GitHub App ID, private key, and installation ID configured for repository access.
The next request to /login shows Google after both credentials are deployed; no separate web flag
or rebuild is required. Google users get the same flat access; their PRs fall back to the App bot
unless the same verified email is also a linked GitHub identity.
Phase 5: Slack App Setup (If Enabled)
Guide user:
- https://api.slack.com/apps → "Create New App" → "From scratch"
- OAuth & Permissions → Add scopes:
assistant:write, app_mentions:read, chat:write,
channels:history, channels:read, groups:history, groups:read, im:history, files:read,
files:write, reactions:write, users:read, users:read.email
- Install to Workspace, note Bot Token (
xoxb-...)
- Basic Information → note Signing Secret
- App Home and Event Subscriptions configured AFTER deployment (worker must be running for URL
verification)
files:read forwards user-attached images into sessions; files:write posts generated media back
to Slack. Reinstall the app whenever either scope is added to an existing installation.
Phase 6: Generate Security Secrets
echo "token_encryption_key: $(openssl rand -base64 32)"
echo "repo_secrets_encryption_key: $(openssl rand -base64 32)"
echo "internal_callback_secret: $(openssl rand -base64 32)"
echo "nextauth_secret: $(openssl rand -base64 32)"
echo "modal_api_secret: $(openssl rand -hex 32)"
echo "github_webhook_secret: $(openssl rand -hex 32)"
Phase 7: Terraform Configuration
Create terraform/environments/production/backend.tfvars:
access_key = "{r2_access_key}"
secret_key = "{r2_secret_key}"
bucket = "open-inspect-{name}-tf-state"
endpoints = {
s3 = "https://{cloudflare_account_id}.r2.cloudflarestorage.com"
}
Create terraform/environments/production/terraform.tfvars with all collected values. Set:
enable_durable_object_bindings = false
enable_service_bindings = false
If GitHub bot is enabled, also set:
enable_github_bot = true
github_webhook_secret = "{generated_value}"
github_bot_username = "{app-slug}[bot]"
Phase 8: Terraform Deployment (Two-Phase)
Important: Build the workers before running Terraform (Terraform references the built bundles):
npm run build -w @open-inspect/control-plane -w @open-inspect/slack-bot -w @open-inspect/github-bot
Phase 1 (bindings disabled):
cd terraform/environments/production
terraform init -backend-config=backend.tfvars
terraform apply
Phase 2 (after Phase 1 succeeds): Update tfvars to set both bindings to true, then:
terraform apply
Phase 9: Complete Slack Setup (If Enabled)
After Terraform deployment, guide user:
The user can apply packages/slack-bot/slack-app-manifest.yaml instead of configuring the following
settings individually. Replace SLACK_EVENTS_URL with the worker's /events URL and
SLACK_INTERACTIONS_URL with its /interactions URL first. The template includes
message.channels and message.groups for channel-message automations; remove them if the
deployment will not use that feature.
OAuth scopes, app installation, the bot token, and the signing secret must be configured before
terraform apply. Apply the URL-dependent manifest after deployment.
Enable Agents
- Agents → Enable the agent feature
- Set the agent description to
AI coding assistant for your codebase
Enable App Home
- App Home → Show Tabs → Enable "Home Tab"
- Enable "Messages Tab" and allow users to send messages
- Save Changes
The App Home provides settings for users' preferred model, reasoning effort, and branch. The
writable Messages tab lets users start direct-message sessions.
Configure Event Subscriptions
- Event Subscriptions → Enable → Request URL from
terraform output -raw slack_bot_events_url
- Wait for "Verified" checkmark
- Subscribe to bot events:
app_home_opened, app_mention, message.channels, message.groups,
message.im
Configure Interactivity
- Interactivity → Enable → Request URL from
terraform output -raw slack_bot_interactions_url
- Select Menus → Use the same URL for Options Load URL. This is required for searchable Slack
repository pickers that use external data sources.
Invite Bot to Channels
- Invite bot to channels:
/invite @BotName
Phase 10: Complete GitHub Bot Setup (If Enabled)
After Terraform deployment, guide user:
Configure Webhook on GitHub App
- Go to GitHub App settings → your app
- Under Webhook: check "Active"
- Webhook URL:
https://open-inspect-github-bot-{deployment_name}.{subdomain}.workers.dev/webhooks/github
- Webhook secret: Enter the
github_webhook_secret value
- Under Subscribe to events, check: Pull requests, Issue comments, Pull request
review comments
- Save changes
Find Bot Username
The bot username is the App's slug with [bot] appended. E.g., if the app is My-Inspect-App, the
bot username is my-inspect-app[bot]. Confirm this matches github_bot_username in
terraform.tfvars.
Usage
- Code Review: Assign the bot as a PR reviewer
- Comment Actions: @mention the bot in a PR comment with instructions
Phase 11: Web App Deployment
npx vercel link --project open-inspect-{deployment_name}
npx vercel --prod
Phase 12: Verification
curl https://open-inspect-control-plane-{deployment_name}.{subdomain}.workers.dev/health
curl https://{workspace}--open-inspect-api-health.modal.run
curl -I "$(terraform output -raw web_app_url)"
Present a deployment summary table. Instruct the user to test: visit the web app, sign in with each
configured provider, create a session, and send a prompt.
Phase 13: CI/CD Setup (Optional)
Ask if user wants GitHub Actions CI/CD. If yes, use gh secret set for all required secrets.
Error Handling
- "redirect_uri is not associated": Callback URL mismatch - update GitHub App settings
- Durable Object errors: Must follow two-phase deployment
- Slack bot not responding: Check Event Subscriptions URL verified, bot invited to channel,
reinstall if scopes changed
- GitHub bot not responding: Check webhook URL, secret,
enable_github_bot = true, and
github_bot_username matches the App's bot login
- Vercel build fails: Terraform configures the monorepo build commands automatically
- "no such file or directory" for dist/index.js: Build workers before Terraform:
npm run build -w @open-inspect/control-plane -w @open-inspect/slack-bot -w @open-inspect/github-bot
- Worker deployment fails: Build shared package first:
npm run build -w @open-inspect/shared
Important Notes
- Track all collected credentials securely throughout the process
- Never log sensitive values
- The callback URL MUST match the actual deployed web app URL
- Two-phase Terraform deployment is required due to Cloudflare Durable Object constraints