Files
CIAgent/scripts/bootstrap-env.sh
T
sakshamandClaude Sonnet 5 d706f226de Fix executable bit on scripts/*.sh, tracked as non-executable since commit
auto-deploy.sh only ever worked because someone chmod +x'd it directly
on the server after cloning, outside git - a fix that lived nowhere
git could see. A `git checkout --` to that file (recovering from an
unrelated direct edit) silently restored the tracked 644 mode,
breaking the deploy timer with "Permission denied" until caught via
journalctl. bootstrap-env.sh had the identical latent bug, just never
triggered since it's only ever run manually.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-06 07:13:42 -04:00

78 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Prepares a real .env for a fresh clone with minimal manual setup:
# 1. Copies .env.example -> .env if .env doesn't exist yet.
# 2. Replaces the three "must be a real random secret, no safe shared
# default" values (JWT_SECRET, API_KEY_ENCRYPTION_SECRET,
# POSTGRES_PASSWORD) with freshly generated ones, but ONLY if they
# still equal the known .env.example placeholder or are blank -
# running this again after you've customized .env is a no-op.
#
# Deliberately does NOT touch DATABASE_URL: docker-compose.prod.yml builds
# it from POSTGRES_USER/POSTGRES_PASSWORD/POSTGRES_DB directly (same as the
# dev compose file already does with its hardcoded values), so there's
# nothing to keep in sync by hand here.
#
# Deliberately does NOT set ANTHROPIC_API_KEY / BRAVE_SEARCH_API_KEY /
# NINJAPEAR_API_KEY / USPTO_API_KEY / TURNSTILE_SITE_KEY / TURNSTILE_SECRET -
# those are meant to be configured after first boot via the app's own
# Settings page ("Your API keys" / "Server secrets"), not .env. See
# KNOWN_LIMITATIONS.md.
set -euo pipefail
cd "$(dirname "$0")/.."
ENV_FILE=".env"
ENV_EXAMPLE=".env.example"
if [ ! -f "$ENV_FILE" ]; then
if [ ! -f "$ENV_EXAMPLE" ]; then
echo "error: $ENV_EXAMPLE not found" >&2
exit 1
fi
cp "$ENV_EXAMPLE" "$ENV_FILE"
echo "Created $ENV_FILE from $ENV_EXAMPLE"
fi
# $1 = var name, $2 = known placeholder value in .env.example, $3 = generator command
_replace_if_placeholder() {
local var_name="$1" placeholder="$2" generator="$3"
local current
current=$(grep -E "^${var_name}=" "$ENV_FILE" | head -n1 | cut -d= -f2-)
if [ -z "$current" ] || [ "$current" = "$placeholder" ]; then
local new_value
new_value=$(eval "$generator")
# Escape characters that are special to sed's replacement text.
local escaped
escaped=$(printf '%s' "$new_value" | sed -e 's/[\/&]/\\&/g')
sed -i "s/^${var_name}=.*/${var_name}=${escaped}/" "$ENV_FILE"
echo "Generated a new ${var_name}"
fi
}
_replace_if_placeholder "JWT_SECRET" "dev-only-change-me-32-characters-minimum" \
"openssl rand -hex 32"
# Fernet requires exactly 32 raw bytes, urlsafe-base64-encoded - plain hex
# would pass silently until the first encrypt/decrypt call, then crash.
_replace_if_placeholder "API_KEY_ENCRYPTION_SECRET" "_wYtsm3nJ070987snBFp2eWVI5pyC0H9gGFUb6Cy4cQ=" \
"openssl rand -base64 32 | tr '+/' '-_'"
_replace_if_placeholder "POSTGRES_PASSWORD" "ciagent" \
"openssl rand -hex 32"
cat <<'EOF'
Done. Before running this for real, still set manually in .env:
- APP_ENV=production, AUTH_MODE=jwt (AUTH_MODE=local refuses to start
when APP_ENV=production - this is an intentional safety check)
- FRONTEND_URL / NEXT_PUBLIC_API_URL / BACKEND_URL for your real domain
- TRUSTED_PROXY_IP_HEADER=CF-Connecting-IP once behind Cloudflare
- RESEND_API_KEY (or SMTP_*) for real security emails - no admin UI for this one
Once the app is running, sign in as the admin account and set these from
the Settings page instead of .env:
- Your API keys: Anthropic, Brave Search, NinjaPear, USPTO
- Server secrets: Cloudflare Turnstile site key + secret
EOF