From 7850b9d34cdbd5a465c81b048dcbec874f27211c Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Sun, 1 Mar 2026 18:06:21 -0800 Subject: [PATCH] feat: move rApp secrets to Infisical, add seed script Remove DATABASE_URL and ADMIN_DIDS from docker-compose.yml (now injected via Infisical entrypoint). Add scripts/seed-infisical.sh to interactively populate 21 module-specific secrets (R2, Immich, Twenty, Discourse, FAL, RunPod, etc.) into the rspace Infisical project. Update Dockerfile to include scripts/ in the image. Co-Authored-By: Claude Opus 4.6 --- Dockerfile | 3 +- docker-compose.yml | 2 - scripts/seed-infisical.sh | 134 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+), 3 deletions(-) create mode 100755 scripts/seed-infisical.sh diff --git a/Dockerfile b/Dockerfile index a070599..fa8946d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,8 @@ RUN mkdir -p /data/communities /data/books /data/swag-artifacts /data/files /dat # Copy entrypoint for Infisical secret injection COPY entrypoint.sh /app/entrypoint.sh -RUN chmod +x /app/entrypoint.sh +COPY scripts/ /app/scripts/ +RUN chmod +x /app/entrypoint.sh /app/scripts/*.sh # Set environment ENV NODE_ENV=production diff --git a/docker-compose.yml b/docker-compose.yml index 811d006..ba7bcd7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,6 @@ services: - INFISICAL_PROJECT_SLUG=rspace - INFISICAL_ENV=prod - INFISICAL_URL=http://infisical:8080 - - DATABASE_URL=postgres://rspace:${POSTGRES_PASSWORD}@rspace-db:5432/rspace - FLOW_SERVICE_URL=http://payment-flow:3010 - FLOW_ID=a79144ec-e6a2-4e30-a42a-6d8237a5953d - FUNNEL_ID=0ff6a9ac-1667-4fc7-9a01-b1620810509f @@ -38,7 +37,6 @@ services: - IMAP_PORT=993 - IMAP_TLS_REJECT_UNAUTHORIZED=false - TWENTY_API_URL=https://rnetwork.online - - ADMIN_DIDS=${ADMIN_DIDS} depends_on: rspace-db: condition: service_healthy diff --git a/scripts/seed-infisical.sh b/scripts/seed-infisical.sh new file mode 100755 index 0000000..545df8d --- /dev/null +++ b/scripts/seed-infisical.sh @@ -0,0 +1,134 @@ +#!/bin/sh +# seed-infisical.sh — Add missing rApp secrets to the rspace Infisical project. +# Run from inside the rspace container: +# docker exec -it rspace-online /app/scripts/seed-infisical.sh +# +# Uses INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_URL, +# INFISICAL_PROJECT_SLUG, and INFISICAL_ENV from the container environment. + +set -e + +INFISICAL_URL="${INFISICAL_URL:-http://infisical:8080}" +INFISICAL_ENV="${INFISICAL_ENV:-prod}" +INFISICAL_PROJECT_SLUG="${INFISICAL_PROJECT_SLUG:-rspace}" + +if [ -z "$INFISICAL_CLIENT_ID" ] || [ -z "$INFISICAL_CLIENT_SECRET" ]; then + echo "ERROR: INFISICAL_CLIENT_ID and INFISICAL_CLIENT_SECRET must be set." + exit 1 +fi + +echo "Authenticating with Infisical at ${INFISICAL_URL}..." + +TOKEN=$(bun -e " +(async () => { + const r = await fetch('${INFISICAL_URL}/api/v1/auth/universal-auth/login', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + clientId: process.env.INFISICAL_CLIENT_ID, + clientSecret: process.env.INFISICAL_CLIENT_SECRET + }) + }).then(r => r.json()); + if (!r.accessToken) { console.error('Auth failed:', JSON.stringify(r)); process.exit(1); } + console.log(r.accessToken); +})(); +") || { echo "Authentication failed"; exit 1; } + +echo "Authenticated. Fetching existing secrets..." + +EXISTING=$(bun -e " +(async () => { + const r = await fetch('${INFISICAL_URL}/api/v3/secrets/raw?workspaceSlug=${INFISICAL_PROJECT_SLUG}&environment=${INFISICAL_ENV}&secretPath=/&recursive=true', { + headers: { 'Authorization': 'Bearer ${TOKEN}' } + }).then(r => r.json()); + if (!r.secrets) { console.error('Failed to list secrets'); process.exit(1); } + console.log(r.secrets.map(s => s.secretKey).join('\n')); +})(); +") || { echo "Failed to list secrets"; exit 1; } + +# Secrets to seed: NAME|MODULE|DESCRIPTION +SECRETS=" +DATABASE_URL|core|Full Postgres connection string (e.g. postgres://rspace:PASS@rspace-db:5432/rspace) +ADMIN_DIDS|core|Comma-separated admin DIDs +R2_ENDPOINT|rTube|Cloudflare R2 S3 endpoint URL +R2_BUCKET|rTube|R2 bucket name (default: rtube-videos) +R2_ACCESS_KEY_ID|rTube|R2 API key ID +R2_SECRET_ACCESS_KEY|rTube|R2 API secret key +R2_PUBLIC_URL|rTube|R2 public CDN URL +RPHOTOS_IMMICH_URL|rPhotos|Internal Immich URL (e.g. http://immich-server:2283) +RPHOTOS_API_KEY|rPhotos|Immich API key +RPHOTOS_IMMICH_PUBLIC_URL|rPhotos|Public Immich URL +TWENTY_API_TOKEN|rNetwork|Twenty CRM API token +HETZNER_API_TOKEN|rForum|Hetzner Cloud API token (Discourse VM provisioning) +CLOUDFLARE_API_TOKEN|rForum|Cloudflare API token (Discourse DNS management) +CLOUDFLARE_FORUM_ZONE_ID|rForum|Cloudflare zone ID for Discourse forum DNS +DISCOURSE_API_KEY|rForum|Discourse admin API key +DISCOURSE_URL|rForum|Discourse instance URL (e.g. https://forum.rforum.online) +FAL_KEY|AI/MI|Fal.ai API key +RUNPOD_API_KEY|AI/MI|RunPod API key +X402_PAY_TO|payments|Payment recipient address +MAILCOW_API_KEY|EncryptID|Mailcow admin API key +ENCRYPTID_DEMO_SPACES|EncryptID|Comma-separated demo space slugs +" + +ADDED=0 +SKIPPED=0 + +echo "" +echo "=== Seeding rspace Infisical secrets ===" +echo "" + +echo "$SECRETS" | while IFS='|' read -r NAME MODULE DESC; do + # Skip blank lines + [ -z "$NAME" ] && continue + # Trim whitespace + NAME=$(echo "$NAME" | xargs) + MODULE=$(echo "$MODULE" | xargs) + DESC=$(echo "$DESC" | xargs) + + # Check if already exists + if echo "$EXISTING" | grep -qx "$NAME"; then + echo "[skip] $NAME (already exists)" + continue + fi + + printf "[%s] %s — %s\n" "$MODULE" "$NAME" "$DESC" + printf " Enter value (or press Enter to skip): " + read -r VALUE + + if [ -z "$VALUE" ]; then + echo " Skipped." + continue + fi + + # Create the secret via Infisical API + RESULT=$(bun -e " +(async () => { + const r = await fetch('${INFISICAL_URL}/api/v3/secrets/raw/$(echo "$NAME" | sed 's/ /%20/g')', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ${TOKEN}', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + workspaceSlug: '${INFISICAL_PROJECT_SLUG}', + environment: '${INFISICAL_ENV}', + secretPath: '/', + secretValue: $(printf '%s' "$VALUE" | bun -e "process.stdout.write(JSON.stringify(await Bun.stdin.text()))") + }) + }); + if (r.ok) console.log('OK'); + else console.log('FAIL: ' + (await r.text())); +})(); + ") || RESULT="FAIL: bun error" + + if [ "$RESULT" = "OK" ]; then + echo " Added." + else + echo " ERROR: $RESULT" + fi +done + +echo "" +echo "Done. Restart the container to pick up new secrets:" +echo " docker compose restart rspace"