Bonus 17: Sichere API-Key-Speicherung (.env + age-Option)
CI / test (push) Canceled after 0s
CI / webapp (push) Canceled after 0s

- .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
This commit is contained in:
Jannik committed 2026-09-05 18:03:04 +02:00
1 parent 67519ee416
commit d7b1acbe33
5 files changed
+272 -1

No files matched your search

+40
View File
@@ -0,0 +1,40 @@
# Mind-o-Mat Secrets Template
#
# Diese Datei ist ein TEMPLATE und enthaelt KEINE echten Werte.
# Kopiere sie zu `.env` und fuelle die echten Werte ein.
# `.env` ist in `.gitignore` und wird NIE committed.
#
# PowerShell:
# Copy-Item .env.example .env
# notepad .env
#
# Bash:
# cp .env.example .env
# nano .env
# === Minimax API (fuer Ingest mit echter KI) ===
# Format: sk-xxxx oder ae-xxxx oder ae-xxxxx (siehe Minimax-Console)
# Hole den Key aus der Minimax-Console > API-Keys.
# WICHTIG: Setze die Env-Variable, die in 99_System/Konfig.md unter
# ki_api_key_env steht (Default: MINIMAX_API_KEY).
MINIMAX_API_KEY=sk-your-real-key-here
# === Vault-Pfad (optional) ===
# Wenn nicht gesetzt, muss --vault <pfad> uebergeben werden
# oder das aktuelle Arbeitsverzeichnis ist ein Vault.
# MINDOMAT_VAULT_PATH=C:\GitHub\Mein-Vault
# === Notes-API Auth (fuer Production-Deployment) ===
# Wenn gesetzt, verlangt die Notes-API einen Bearer-Token.
# Token-Format: <timestamp>.<hmac-sha256-hex>
# Generiere mit: mindomat-token-gen
# NOTES_API_TOKEN=your-secret-here
# NOTES_API_ALLOWED_ORIGINS=https://your-vault.example.com,https://your-app.example.com
# === gitea-Deployment ===
# Gitea-URL fuer das Vault-Repo (fuer sync)
# GITEA_REMOTE=https://git.orfel.de/Jannik/Mein-Vault.git
# === QMD (optional, normalerweise ueber CLI) ===
# QMD-Cache-Pfad (Default: 99_System/Cache)
# QMD_CACHE_PATH=99_System/Cache
+20
View File
@@ -39,3 +39,23 @@ vitest.log.err
# Tool-Build-Output
dist/
*.tsbuildinfo
# Secrets und Credentials (NIEMALS committen)
.env
.env.local
.env.*.local
secrets.json
secrets.age
secrets.key
*.pem
*.key
*.p12
# Test-Output und Coverage-Reports
coverage/
.nyc_output/
# Temporaere Verzeichnisse
tmp/
temp/
.cache/
+188
View File
@@ -0,0 +1,188 @@
# 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
```powershell
# 1. Template kopieren
Copy-Item .env.example .env
# 2. .env editieren und echte Werte einsetzen
notepad .env
```
```bash
# Bash:
cp .env.example .env
nano .env
```
### Inhalt (Beispiel)
```dotenv
# 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:
```bash
npm install --save-dev dotenv
```
Dann in `bin/mindomat.mjs`:
```javascript
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`:
```yaml
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:
```powershell
# 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`](https://github.com/FiloSottile/age):
### Setup
```bash
# 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.
+3 -1
View File
@@ -30,7 +30,9 @@
"init-vault": "npm run build && node bin/mindomat.mjs init-vault",
"index": "npm run build && node bin/mindomat.mjs index",
"ingest": "npm run build && node bin/mindomat.mjs ingest",
"sync": "npm run build && node bin/mindomat.mjs sync"
"sync": "npm run build && node bin/mindomat.mjs sync",
"token-gen": "node scripts/token-gen.mjs",
"build:twa": "node scripts/build-twa.mjs"
},
"keywords": [
"second-brain",
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env node
// Token-Generator fuer die Notes-API (Phase 9: Auth)
// Generiert ein Bearer-Token im Format: <timestamp>.<hmac-sha256-hex>
import { createHmac } from 'node:crypto';
const secret = process.env.NOTES_API_TOKEN;
if (!secret) {
console.error('Fehler: NOTES_API_TOKEN nicht gesetzt.');
console.error('Setze die Env-Variable, z. B.:');
console.error(' $env:NOTES_API_TOKEN = "my-secret" # PowerShell');
console.error(' export NOTES_API_TOKEN=my-secret # Bash');
process.exit(1);
}
const ts = Date.now().toString();
const hmac = createHmac('sha256', secret).update(ts).digest('hex');
const token = `${ts}.${hmac}`;
console.log(token);
console.error(`(Token gueltig fuer Bearer-Auth. Secret: ${'*'.repeat(8)})`);