Files
Jannik d7b1acbe33
CI / test (push) Canceled after 0s
CI / webapp (push) Canceled after 0s
Bonus 17: Sichere API-Key-Speicherung (.env + age-Option)
- .gitignore: Secrets (.env, *.key, *.pem) + Coverage + Temp-Verzeichnisse
- .env.example: Template ohne echte Werte, mit Anleitung
- scripts/token-gen.mjs: HMAC-Bearer-Token-Generator
- package.json: token-gen + build:twa Scripts
- docs/SECRETS.md: komplette Anleitung
  - Lokale Entwicklung (.env)
  - CI (gitea-Repository-Secrets)
  - age-Verschluesselung als optionale Schicht
  - Was NIE tun / was IMMER tun
  - Incident-Response bei geleaktem Secret

Verifiziert:
- token-gen erzeugt Token korrekt (stderr: Token gueltig...)
- 122/122 Tests gruen
- .env.example im Repo, .env ist gitignored
2026-09-05 18:03:04 +02:00

5.7 KiB

Secrets und Credentials

Diese Anleitung erklärt, wie du sensible Daten (API-Keys, Tokens, Passwörter) in Mind-o-Mat handhabst, ohne sie jemals in Git zu committen.

Überblick

Mind-o-Mat nutzt drei Arten von Secrets:

Secret Wo gespeichert Wer sieht es
Minimax API-Key .env (lokal) / CI-Secret nur Tool-Code zur Laufzeit
Notes-API Token .env (lokal) / CI-Secret Browser + Tool
gitea-Credentials git credential helper / SSH-Key nur git push

Grundregel: Secrets kommen in .env (gitignored) oder in CI-Secrets (verschlüsselt), niemals ins Repo.

1. Lokale Entwicklung: .env-Datei

Einrichten

# 1. Template kopieren
Copy-Item .env.example .env

# 2. .env editieren und echte Werte einsetzen
notepad .env
# Bash:
cp .env.example .env
nano .env

Inhalt (Beispiel)

# Minimax API-Key (aus Minimax-Console)
MINIMAX_API_KEY=sk-abc123...

# Notes-API Auth (fuer Production-Deployment, optional)
NOTES_API_TOKEN=my-secret-for-hmac
NOTES_API_ALLOWED_ORIGINS=https://my-vault.example.com

# Vault-Pfad (optional)
MINDOMAT_VAULT_PATH=C:\GitHub\Mein-Vault

Wie der Code die Werte liest

Das Tool nutzt keine .env-Loader-Library direkt. Stattdessen:

  1. System-Env-Variablen werden automatisch von Node.js gelesen (process.env)
  2. PowerShell: $env:MINIMAX_API_KEY = "sk-..." setzt für die aktuelle Session
  3. Bash: export MINIMAX_API_KEY=sk-... setzt für die aktuelle Session
  4. .env ist NICHT automatisch geladen — wenn du node bin/mindomat.mjs direkt aufrufst, musst du die Env-Variable vorher setzen

Automatisches Laden (optional)

Wenn du dotenv als dev-Dependency willst:

npm install --save-dev dotenv

Dann in bin/mindomat.mjs:

import 'dotenv/config';
// ... rest

Aber das ist nicht nötig für die Standard-Verwendung. PowerShell-User rufen ohnehin direkt auf.

2. CI (gitea-Actions): Repository-Secrets

In deiner gitea-Instanz: Repository → Settings → Secrets → New Secret

Füge hinzu:

Name Wert
MINIMAX_API_KEY dein API-Key
NOTES_API_TOKEN dein HMAC-Secret
NOTES_API_ALLOWED_ORIGINS z. B. https://vault.example.com

In .gitea/workflows/ci.yml:

jobs:
  test:
    env:
      MINIMAX_API_KEY: ${{ secrets.MINIMAX_API_KEY }}

Die Secrets sind in CI verschlüsselt und werden zur Laufzeit als Env-Variablen bereitgestellt.

3. Notes-API Bearer-Token generieren

Wenn du die Notes-API mit Auth nutzt, brauchst du Bearer-Tokens:

# 1. Secret setzen
$env:NOTES_API_TOKEN = "my-secret"

# 2. Token generieren
npm run token-gen
# Output: 1725523000000.4a7b2c1d... (Beispiel)

# 3. Token in der Webapp nutzen
# In .env: NOTES_API_TOKEN=my-secret
# Beim Browser-Request: Authorization: Bearer <token>

Token ist 1 Stunde gültig (Timestamp-basiert). Für längere Gültigkeit timestamp durch ein Ablauf-Datum ersetzen.

4. Optional: age-Verschlüsselung für CI

Wenn du Secrets verschlüsselt im Repo speichern willst (z. B. für lokale CI ohne externe Secret-Store), nutze age:

Setup

# 1. age installieren
# Windows: scoop install age
# macOS: brew install age
# Linux: apt install age

# 2. Schluesselpaar generieren
age-keygen -o key.txt   # privater Schluessel (NICHT in Git!)
mkdir -p .age
age-keygen -y key.txt > .age/recipients.pub   # oeffentlicher Schluessel (in Git)

# 3. Secret verschluesseln
$env:MINIMAX_API_KEY = "sk-abc123..."
node -e "
const { execSync } = require('node:child_process');
const value = process.env.MINIMAX_API_KEY;
execSync('echo ' + value + ' | age -R .age/recipients.pub -o secrets.age', { stdio: 'inherit' });
"

# 4. secrets.age committen (verschluesselt, sicher)
git add secrets.age
git commit -m "Add encrypted Minimax API key"

# 5. Zur Laufzeit entschluesseln
age -d -i key.txt secrets.age   # in CI mit Secret-Storage fuer key.txt

Vorteile

  • secrets.age ist im Git, aber verschlüsselt (öffentlich lesbar, aber nicht entschlüsselbar ohne key.txt)
  • Funktioniert in CI ohne externe Secret-Stores
  • key.txt bleibt in deinem lokalen ~/.ssh/age/ oder in CI-Secret-Storage

Nachteile

  • Komplexer als Env-Variablen
  • key.txt muss separat gesichert werden (sonst sind die Secrets verloren)

Empfehlung: Nutze age NUR, wenn du Secrets im Repo versionieren willst (z. B. für Demo-Setups). Für Production-CI sind gitea-Repository-Secrets einfacher.

5. Was du NIEMALS tun solltest

  • ❌ API-Key direkt in package.json oder src/ hardcoden
  • ❌ .env ins Git committen (ist in .gitignore, aber prüfe!)
  • ❌ Secrets in Commit-Messages oder PR-Beschreibungen erwähnen
  • ❌ Secrets in Screenshots oder Logs zeigen
  • ❌ Secrets in JSON-Dateien oder Markdown-Dateien im Repo

6. Was du TUN solltest

  • ✅ .env aus .env.example lokal erstellen
  • ✅ Secrets über PowerShell $env:... oder Bash export ... setzen
  • ✅ Für CI: gitea-Repository-Secrets nutzen
  • ✅ Vor jedem Commit: git status und git diff prüfen, dass keine Secrets drin sind
  • ✅ Falls ein Secret geleakt wurde: sofort widerrufen und neu generieren

7. Falls ein Secret geleakt wurde

  1. Sofort widerrufen im jeweiligen Service (Minimax-Console, gitea, etc.)
  2. Neuen Key generieren
  3. Aus Git-History entfernen (mit git filter-repo oder BFG Repo-Cleaner)
  4. Force-Push zum Remote (falls öffentlich: alle Klone sind kompromittiert)
  5. Alle Verbraucher aktualisieren (CI-Secrets, lokale .env-Dateien)
  6. Audit-Log prüfen, ob der geleakte Key bereits missbraucht wurde

Im Worst-Case: Vault-Daten neu anlegen, alle Tokens rotieren.