diff --git a/.gitignore b/.gitignore index 6538287..d17e492 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Environment files (contain secrets) **/.env +services/shared.env # AGE private key (never commit) **/age-key.txt diff --git a/README.md b/README.md index 12e2a64..249f1fa 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,21 @@ Das Skript ist idempotent und kümmert sich um: ## Umgebungsvariablen hinzufügen oder ändern -Die Umgebungsvariablen liegen lokal unter `services/paperclip/.env`. Da dieses File per `.gitignore` ausgeschlossen ist (aus Sicherheitsgründen), wird stattdessen `.env.age` in das Repository gepusht. +Es gibt zwei Arten von `.env`-Dateien: -Sobald du Änderungen an deiner lokalen `.env` machst, musst du diese neu verschlüsseln, bevor du den Commit erstellst: +| Datei | Inhalt | Gilt für | +|---|---|---| +| `services/shared.env(.age)` | **API Keys** (OpenRouter, Mistral, etc.) | **Alle** Services | +| `services//.env(.age)` | Service-spezifische Secrets (DB-Passwort, Auth-Secret) | Nur dieser Service | + +Da `.env`-Dateien per `.gitignore` ausgeschlossen sind, wird stattdessen jeweils die `.age`-Variante ins Repository gepusht. Nach Änderungen neu verschlüsseln: ```bash -age -r -o services/paperclip/.env.age services/paperclip/.env +# Geteilte API Keys (alle Services) +age -r -o services/shared.env.age services/shared.env + +# Service-spezifisch (z. B. PaperClip) +age -r -o services/paperclip/.env.age services/paperclip/.env ``` ## LLM Konfiguration & Onboarding Workaround diff --git a/deploy_script.sh b/deploy_script.sh index dc89f25..b333536 100644 --- a/deploy_script.sh +++ b/deploy_script.sh @@ -320,32 +320,49 @@ stop_all_services() { # 7. Secrets & Data ############################################################################### decrypt_envs() { - log "Decrypting .env.age files..." + # ------------------------------------------------------------------------- + # 1. Geteilte API Keys entschlüsseln (Single Source of Truth) + # ------------------------------------------------------------------------- + local shared_env="${SERVICES_DIR}/shared.env" + local shared_env_age="${SERVICES_DIR}/shared.env.age" + if [[ -f "${shared_env_age}" ]]; then + log "Decrypting shared API keys (shared.env.age)..." + age --decrypt -i "${AGE_PRIVATE_KEY}" -o "${shared_env}" "${shared_env_age}" + sed -i 's/\r$//' "${shared_env}" + chmod 600 "${shared_env}" + else + warn "shared.env.age not found — no shared API keys loaded." + fi + + # ------------------------------------------------------------------------- + # 2. Service-spezifische .env.age Dateien entschlüsseln + # ------------------------------------------------------------------------- + log "Decrypting service-specific .env.age files..." for svc_name in "${ACTIVE_SERVICES[@]}"; do local svc_dir="${SERVICES_DIR}/${svc_name}" if [[ -f "${svc_dir}/.env.age" ]]; then age --decrypt -i "${AGE_PRIVATE_KEY}" -o "${svc_dir}/.env" "${svc_dir}/.env.age" sed -i 's/\r$//' "${svc_dir}/.env" - + # Ensure BETTER_AUTH_SECRET is set for Paperclip if [[ "${svc_name}" == "paperclip" ]]; then if ! grep -q "BETTER_AUTH_SECRET" "${svc_dir}/.env"; then log "Generating missing BETTER_AUTH_SECRET in .env..." echo "BETTER_AUTH_SECRET=\"$(openssl rand -hex 32)\"" >> "${svc_dir}/.env" fi - - # Auto-configure default LLM to bypass UI Onboarding + + # Auto-configure default LLM (OPENAI_* Overrides) aus shared.env lesen if ! grep -q "OPENAI_API_KEY=" "${svc_dir}/.env" && ! grep -q "ANTHROPIC_API_KEY=" "${svc_dir}/.env"; then - if grep -q "OPENROUTER_API_KEY=" "${svc_dir}/.env"; then + if [[ -f "${shared_env}" ]] && grep -q "OPENROUTER_API_KEY=" "${shared_env}"; then log "Auto-configuring OpenRouter as default LLM..." echo "" >> "${svc_dir}/.env" echo "OPENAI_BASE_URL=https://openrouter.ai/api/v1" >> "${svc_dir}/.env" - grep "OPENROUTER_API_KEY=" "${svc_dir}/.env" | sed 's/OPENROUTER_API_KEY=/OPENAI_API_KEY=/' >> "${svc_dir}/.env" - elif grep -q "MISTRAL_API_KEY=" "${svc_dir}/.env"; then + grep "OPENROUTER_API_KEY=" "${shared_env}" | sed 's/OPENROUTER_API_KEY=/OPENAI_API_KEY=/' >> "${svc_dir}/.env" + elif [[ -f "${shared_env}" ]] && grep -q "MISTRAL_API_KEY=" "${shared_env}"; then log "Auto-configuring Mistral as default LLM..." echo "" >> "${svc_dir}/.env" echo "OPENAI_BASE_URL=https://api.mistral.ai/v1" >> "${svc_dir}/.env" - grep "MISTRAL_API_KEY=" "${svc_dir}/.env" | sed 's/MISTRAL_API_KEY=/OPENAI_API_KEY=/' >> "${svc_dir}/.env" + grep "MISTRAL_API_KEY=" "${shared_env}" | sed 's/MISTRAL_API_KEY=/OPENAI_API_KEY=/' >> "${svc_dir}/.env" fi fi fi diff --git a/services/hermes/.env.age b/services/hermes/.env.age deleted file mode 100644 index fe5691b..0000000 Binary files a/services/hermes/.env.age and /dev/null differ diff --git a/services/hermes/docker-compose.yml b/services/hermes/docker-compose.yml index 9347129..11850c0 100644 --- a/services/hermes/docker-compose.yml +++ b/services/hermes/docker-compose.yml @@ -2,8 +2,8 @@ services: hermes: image: nousresearch/hermes-agent:latest container_name: hermes - environment: - OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}" + env_file: + - ../shared.env # Geteilte API Keys (OpenRouter, Mistral, etc.) volumes: - /opt/PaperClip-Data/hermes:/opt/data networks: diff --git a/services/paperclip/.env.age b/services/paperclip/.env.age index 442366a..6a1bd48 100644 Binary files a/services/paperclip/.env.age and b/services/paperclip/.env.age differ diff --git a/services/paperclip/docker-compose.yml b/services/paperclip/docker-compose.yml index 3841ad9..3493137 100644 --- a/services/paperclip/docker-compose.yml +++ b/services/paperclip/docker-compose.yml @@ -21,6 +21,9 @@ services: image: ghcr.io/paperclipai/paperclip:latest container_name: paperclip_app pids_limit: 2048 + env_file: + - ../shared.env # Geteilte API Keys (OpenRouter, Mistral, etc.) + - .env # Service-spezifisch (DB-Passwort, Auth-Secret, OPENAI_* Overrides) environment: DATABASE_URL: postgres://paperclip:${POSTGRES_PASSWORD}@paperclip-postgres:5432/paperclip PORT: "3100" @@ -31,13 +34,6 @@ services: PAPERCLIP_DEPLOYMENT_EXPOSURE: "public" PAPERCLIP_AUTH_BASE_URL_MODE: "explicit" PAPERCLIP_PUBLIC_URL: "https://paperclip.orfel.de" - BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET}" - OPENAI_API_KEY: "${OPENAI_API_KEY}" - OPENAI_BASE_URL: "${OPENAI_BASE_URL}" - OPENROUTER_API_KEY: "${OPENROUTER_API_KEY}" - MISTRAL_API_KEY: "${MISTRAL_API_KEY}" - MINIMAX_API_KEY: "${MINIMAX_API_KEY}" - POE_API_KEY: "${POE_API_KEY}" PAPERCLIP_OPENCODE_PROVIDERS: '{"mistral":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:MISTRAL_API_KEY}"},"models":{"mistral/mistral-large":{}}},"minimax":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:MINIMAX_API_KEY}"},"models":{"minimax/abab6.5s":{}}},"poe":{"npm":"@ai-sdk/openai-compatible","options":{"apiKey":"{env:POE_API_KEY}"},"models":{"poe/claude-3-opus":{}}}}' volumes: - /opt/PaperClip-Data/paperclip:/paperclip diff --git a/services/shared.env.age b/services/shared.env.age new file mode 100644 index 0000000..43f8b56 Binary files /dev/null and b/services/shared.env.age differ