#!/usr/bin/env bash# One-shot deploy for the Executor Cloudflare host.## Provisions everything a fresh account needs and deploys the Worker:# 1. verifies wrangler is logged in# 2. creates (or reuses) the `executor` D1 database and writes its id into# wrangler.jsonc# 3. generates + uploads EXECUTOR_SECRET_KEY (the at-rest secret key) if unset# 4. deploys the Worker# 5. prints the single manual step: configure the Cloudflare Access application## Idempotent — safe to re-run. Run from anywhere:# bash apps/host-cloudflare/scripts/deploy.shset -euo pipefailSCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"CONFIG="$APP_DIR/wrangler.jsonc"cd "$APP_DIR"step() { printf '\n\033[1;36m==> %s\033[0m\n' "$1"; }info() { printf ' %s\n' "$1"; }step "Checking wrangler login"if ! bunx wrangler whoami >/dev/null 2>&1; then info "Not logged in. Run: bunx wrangler login" exit 1fiinfo "Logged in."step "Provisioning D1 database 'executor'"# `d1 create` is non-idempotent (errors if it exists), so list first.EXISTING_ID="$(bunx wrangler d1 list --json 2>/dev/null \ | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const r=JSON.parse(s).find(d=>d.name==="executor");process.stdout.write(r?r.uuid:"")}catch{}})')"if [ -n "$EXISTING_ID" ]; then DB_ID="$EXISTING_ID" info "Reusing existing database: $DB_ID"else CREATE_OUT="$(bunx wrangler d1 create executor 2>&1)" DB_ID="$(printf '%s' "$CREATE_OUT" | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' | head -1)" info "Created database: $DB_ID"fi[ -n "$DB_ID" ] || { echo "Failed to resolve D1 database id" >&2; exit 1; }step "Writing D1 id into wrangler.jsonc"# Replace whatever database_id is present (placeholder or a prior id).node -e ' const fs=require("fs"),p=process.argv[1],id=process.argv[2]; let t=fs.readFileSync(p,"utf8"); t=t.replace(/("database_id":\s*")[^"]*(")/, `$1${id}$2`); fs.writeFileSync(p,t);' "$CONFIG" "$DB_ID"info "wrangler.jsonc -> $DB_ID"step "Ensuring EXECUTOR_SECRET_KEY secret"if bunx wrangler secret list 2>/dev/null | grep -q EXECUTOR_SECRET_KEY; then info "Secret already set — leaving it."else SECRET="$(node -e 'console.log(require("node:crypto").randomBytes(32).toString("hex"))')" printf '%s' "$SECRET" | bunx wrangler secret put EXECUTOR_SECRET_KEY >/dev/null info "Generated + uploaded a fresh 32-byte key."fistep "Building the web SPA"bunx vite buildstep "Deploying Worker"bunx wrangler deploycat <<'NEXT'==> One manual step left: turn on Cloudflare Access (the auth layer) The Worker is deployed but is not ready to serve requests until you configure a Cloudflare Access application. API and MCP requests return 503 and name the missing variables until configuration is complete. In the Zero Trust dashboard: 1. Access -> Applications -> Add an application -> Self-hosted 2. Application domain: executor-cloudflare.<your-subdomain>.workers.dev 3. Add an Access policy (e.g. "Emails ending in @yourcompany.com") 4. After saving, copy the Application Audience (AUD) tag, then set: bunx wrangler deploy --var ACCESS_AUD:<aud> \ --var ACCESS_TEAM_DOMAIN:<your-team>.cloudflareaccess.com \ --var ADMIN_EMAILS:<admin@example.com> Wrangler preserves these live variables during later code deploys. That's it. Visiting the Worker URL now prompts a Cloudflare Access login, and the Worker validates the issued JWT on every request.NEXT
#!/usr/bin/env bash
# One-shot deploy for the Executor Cloudflare host.
#
# Provisions everything a fresh account needs and deploys the Worker:
# 1. verifies wrangler is logged in
# 2. creates (or reuses) the `executor` D1 database and writes its id into
# wrangler.jsonc
# 3. generates + uploads EXECUTOR_SECRET_KEY (the at-rest secret key) if unset
# 4. deploys the Worker
# 5. prints the single manual step: configure the Cloudflare Access application
#
# Idempotent — safe to re-run. Run from anywhere:
# bash apps/host-cloudflare/scripts/deploy.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
CONFIG="$APP_DIR/wrangler.jsonc"
cd "$APP_DIR"
step() { printf '\n\033[1;36m==> %s\033[0m\n' "$1"; }
info() { printf ' %s\n' "$1"; }
step "Checking wrangler login"
if ! bunx wrangler whoami >/dev/null 2>&1; then
info "Not logged in. Run: bunx wrangler login"
exit 1
fi
info "Logged in."
step "Provisioning D1 database 'executor'"
# `d1 create` is non-idempotent (errors if it exists), so list first.
EXISTING_ID="$(bunx wrangler d1 list --json 2>/dev/null \
| node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{const r=JSON.parse(s).find(d=>d.name==="executor");process.stdout.write(r?r.uuid:"")}catch{}})')"
if [ -n "$EXISTING_ID" ]; then
DB_ID="$EXISTING_ID"
info "Reusing existing database: $DB_ID"
else
CREATE_OUT="$(bunx wrangler d1 create executor 2>&1)"
DB_ID="$(printf '%s' "$CREATE_OUT" | grep -oE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' | head -1)"
info "Created database: $DB_ID"
fi
[ -n "$DB_ID" ] || { echo "Failed to resolve D1 database id" >&2; exit 1; }
step "Writing D1 id into wrangler.jsonc"
# Replace whatever database_id is present (placeholder or a prior id).
node -e '
const fs=require("fs"),p=process.argv[1],id=process.argv[2];
let t=fs.readFileSync(p,"utf8");
t=t.replace(/("database_id":\s*")[^"]*(")/, `$1${id}$2`);
fs.writeFileSync(p,t);
' "$CONFIG" "$DB_ID"
info "wrangler.jsonc -> $DB_ID"
step "Ensuring EXECUTOR_SECRET_KEY secret"
if bunx wrangler secret list 2>/dev/null | grep -q EXECUTOR_SECRET_KEY; then
info "Secret already set — leaving it."
else
SECRET="$(node -e 'console.log(require("node:crypto").randomBytes(32).toString("hex"))')"
printf '%s' "$SECRET" | bunx wrangler secret put EXECUTOR_SECRET_KEY >/dev/null
info "Generated + uploaded a fresh 32-byte key."
fi
step "Building the web SPA"
bunx vite build
step "Deploying Worker"
bunx wrangler deploy
cat <<'NEXT'
==> One manual step left: turn on Cloudflare Access (the auth layer)
The Worker is deployed but is not ready to serve requests until you configure
a Cloudflare Access application. API and MCP requests return 503 and name the
missing variables until configuration is complete. In the Zero Trust
dashboard:
1. Access -> Applications -> Add an application -> Self-hosted
2. Application domain: executor-cloudflare.<your-subdomain>.workers.dev
3. Add an Access policy (e.g. "Emails ending in @yourcompany.com")
4. After saving, copy the Application Audience (AUD) tag, then set:
bunx wrangler deploy --var ACCESS_AUD:<aud> \
--var ACCESS_TEAM_DOMAIN:<your-team>.cloudflareaccess.com \
--var ADMIN_EMAILS:<admin@example.com>
Wrangler preserves these live variables during later code deploys.
That's it. Visiting the Worker URL now prompts a Cloudflare Access login,
and the Worker validates the issued JWT on every request.
NEXT