diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..31af80c --- /dev/null +++ b/.env.example @@ -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 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: . +# 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 diff --git a/.gitignore b/.gitignore index 6758fe4..1b07808 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/docs/SECRETS.md b/docs/SECRETS.md new file mode 100644 index 0000000..925a7a0 --- /dev/null +++ b/docs/SECRETS.md @@ -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 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. diff --git a/package.json b/package.json index 1733062..7660112 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/token-gen.mjs b/scripts/token-gen.mjs new file mode 100644 index 0000000..8da8b04 --- /dev/null +++ b/scripts/token-gen.mjs @@ -0,0 +1,21 @@ +#!/usr/bin/env node +// Token-Generator fuer die Notes-API (Phase 9: Auth) +// Generiert ein Bearer-Token im Format: . + +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)})`);