refactor: Single source of truth for API keys via services/shared.env
- NEW: services/shared.env.age — encrypted shared API keys (OpenRouter, Mistral, Minimax, Poe) - REMOVE: services/hermes/.env.age — no service-specific secrets needed - UPDATE: services/paperclip/.env.age — now only DB password + auth secret - UPDATE: docker-compose.yml (paperclip + hermes) — use env_file for shared.env - UPDATE: deploy_script.sh — decrypt shared.env.age first, read API keys from there - UPDATE: .gitignore — add services/shared.env - UPDATE: README.md — document shared.env pattern
This commit is contained in:
1 parent
2df8b29470
commit
60d48a43b4
8 files changed
+43
-20
No files matched your search
@@ -1,5 +1,6 @@
|
||||
# Environment files (contain secrets)
|
||||
**/.env
|
||||
services/shared.env
|
||||
|
||||
# AGE private key (never commit)
|
||||
**/age-key.txt
|
||||
|
||||
@@ -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/<name>/.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 <DEIN_AGE_PUBLIC_KEY> -o services/paperclip/.env.age services/paperclip/.env
|
||||
# Geteilte API Keys (alle Services)
|
||||
age -r <AGE_PUBLIC_KEY> -o services/shared.env.age services/shared.env
|
||||
|
||||
# Service-spezifisch (z. B. PaperClip)
|
||||
age -r <AGE_PUBLIC_KEY> -o services/paperclip/.env.age services/paperclip/.env
|
||||
```
|
||||
|
||||
## LLM Konfiguration & Onboarding Workaround
|
||||
|
||||
+25
-8
@@ -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
|
||||
|
||||
Binary file not shown.
@@ -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:
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user