trading-system-docs/tolaria/tolaria-write-auth/AUTH3B_DESIGN.md
Red Queen 10761f52b2 AUTH.3E: Executor Command Channel + Runtime Boundary Contract
- job_schema: geschlossene Job-Type-Allowlist (C5_SAVE_OBJECT/C5_DELETE_OBJECT), Pfad-/Längen-Validierung
- job_state_machine: deterministische States (CREATED/READY/CLAIMED/EXECUTING/SUCCEEDED/FAILED)
- job_claim: atomare Claim-/Lease-/Recovery-Logik (kein TOCTOU)
- job_store: getrennte SQLite-Inbox-DBs (c5a_save.db/c5a_delete.db), delegiert an job_claim
- save_executor_core: SAVE-only, Content-Rekonstruktion, Provenance-Validierung
- delete_executor_core: DELETE-only, AUTH.3D-Composition, TOCTOU-Defense (Re-Read nach Claim)
- test_job_channel: T1-T40 + adversarial (72 Tests)
- test_job_channel_adversarial: adversarial + Substitution + DB-Manipulation
- sensitivity_proof_auth3e: Mutationen A-L (12/12 Invarianten PRESENT)
- AUTH3B/3C/3D/3E_DESIGN: autoritative Security-Dokumentation (e25 Reconciliation)

COMMAND != AUTHORIZATION. Kein generischer Dispatcher. RQ credential-free.
Keine produktive Mutation. Keine echten Credentials.
2026-08-27 10:03:32 +00:00

711 lines
40 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AUTH.3B — SECRET / ENV / RUNTIME INJECTION DESIGN + DEPLOYMENT PRE-FLIGHT
**Status:** DESIGN + VERIFIED DEPLOYMENT PLAN (STRICT READ-ONLY, KEIN produktives Deployment)
**Datum:** 2026-08-27
**Mission Type:** STRICT DESIGN + READ-ONLY DISCOVERY + ISOLATED TESTS
**Baseline:** AUTH.1 (40bbc40) + AUTH.2 (edcb4902) + AUTH.3A (a11c1bb) = COMPLETE
> ⚠️ Dies ist ein **Design-/Plan-Dokument**. Es wird NICHTS produktiv deployt,
> kein Token erzeugt, keine ENV/Compose/Coolify geändert, kein Container
> restart/recreate, keine produktive Tolaria-Auth aktiviert.
---
## 1. RUNTIME DISCOVERY (Phase 1) — IST-Zustand
### A) Tolaria Server
| Attribut | Wert |
|---|---|
| Container | `tolaria` |
| Runtime | Node.js 20 / Vite (dev server, `vite.config.ts` middleware) |
| Host Source | `/opt/tolaria/app/tolaria` |
| Container Source | `/app/tolaria` |
| Vault | `/opt/data/tolaria-vault``/app/vault` (Bind-Mount) |
| Compose | `/opt/tolaria/docker-compose.yml` |
| Port | 5173 (produktiv); Repo-`vite.config.ts` deklariert 5202 (dev) |
| Produktiver Source-Basisstand | `b5a5e4bd` (VOR AUTH.2) |
| AUTH.2 deployed? | **NEIN** — Produktion läuft noch OHNE neue Auth (verifiziert: save ohne Token → 200) |
| ENV erwartet (Code) | `TOLARIA_SAVE_TOKEN`, `TOLARIA_DELETE_TOKEN`, `TOLARIA_VAULT_ROOT` (Default `/app/vault`) |
| Fail-closed | JA — `authorizeVaultWrite` Z.120-123: leeres `expected` → 401 "Write scope not configured" |
### B) C5C Writer (SAVE)
| Attribut | Wert |
|---|---|
| Klasse | `C5CPropagator` (`rq_c5c.py` Z.497) |
| Write-Methode | `TolariaClient.write()` (Z.315) → POST `/api/vault/save`, Bearer mit `save_token` |
| Credential-Quelle | NUR explizit injizierter Client (`client=...`); Default `TolariaClient()` credential-frei → fail-closed |
| ENV | `TOLARIA_SAVE_TOKEN` (Konstante Z.100); `rq_c5c.py` liest ENV NICHT selbst |
| CLI-Entrypoint | **KEINER** — kein CLI-Command ruft C5CPropagator mit save_token auf |
| Produktiver Aufrufer | `rq_c5f_resume.py` (untracked) — konstruiert `TolariaClient()` OHNE save_token → mit AUTH.3A fail-closed |
| Runtime | RQ-Container (`06a4a73573d0`), C5-DB `/opt/data/c5c_live/c5a.db` |
### C) C5 DeleteExecutor (DELETE)
| Attribut | Wert |
|---|---|
| Klasse | `DeleteExecutor` (`rq_c5_delete.py`) |
| Write-Methode | `TolariaClient.delete()` → POST `/api/vault/delete`, Bearer mit `delete_token` |
| Credential-Quelle | `_delete_executor` (`rq_c5_cli.py` Z.344): `delete_token=os.environ.get(ENV_TOLARIA_DELETE_TOKEN)` |
| ENV | `TOLARIA_DELETE_TOKEN` (Konstante Z.101) |
| CLI-Entrypoint | `c5-delete-execute`, `c5-delete-replay` (Human-Gated) |
| Runtime | RQ-Container (`06a4a73573d0`), C5-DB `/opt/data/c5c_live/c5a.db` |
### D) Red Queen / Hermes
| Attribut | Wert |
|---|---|
| Container | `06a4a73573d0` (RQ/Hermes) |
| C5-Caller | laufen IM RQ-Container (C5-DB `/opt/data/c5c_live/c5a.db`) |
| Docker-Daemon | NICHT verfügbar aus RQ-Container (kein `/var/run/docker.sock`) |
| VPS-Host-SSH | NICHT verfügbar (Key `red_queen_forgejo` nur für Forgejo-Git-Push) |
| Tolaria-API | read-only erreichbar (`187.124.31.123:5173`) |
### E) Rain / OpenClaw
| Attribut | Wert |
|---|---|
| Ports | 55163 (Alice), 54524 (Matt Addison) |
| Zugriff auf C5-DB | NICHT verifiziert (separate Container) |
| Tolaria-Write-Credential | KEIN (Architektur-Invariante) |
---
## 2. KRITISCHER BEFUND (Phase 7-Vorab)
**SAVE-Caller (C5CPropagator) und DELETE-Caller (DeleteExecutor) laufen im SELBEN
Prozess/Container (RQ-Container `06a4a73573d0`).** Es existiert KEINE getrennte
Runtime-Boundary zwischen SAVE und DELETE. Beide Credentials würden, wenn beide
injiziert würden, im selben Container landen.
**Konsequenz:** Die Credential-Separation (SAVE≠DELETE) ist heute NUR logisch
(verschiedene ENV-Namen, verschiedene Klassen), NICHT technisch als getrennte
Security Boundaries. Das muss in Phase 7 explizit dokumentiert und in Phase 3/4
bewertet werden.
---
## 2b. SECURITY BOUNDARY ANALYSIS (Phase 2) — IST-Zustand
### Runtime-Fakten (verifiziert)
- **RQ/Hermes-Container** = `06a4a73573d0`, User `hermes` (uid 10010, gid 10000).
- **Kein Docker-Socket** im RQ-Container (`/var/run/docker.sock` fehlt) → RQ kann
keine anderen Container inspizieren.
- **Kein VPS-Host-SSH** (Key `red_queen_forgejo` nur für Forgejo-Git-Push) → RQ kann
Host-Dateien (`/opt/tolaria`, Compose) NICHT lesen.
- **Kein `/opt/tolaria`** im RQ-Container-Dateisystem.
- **/proc** im RQ-Container zeigt nur eigene Prozesse (24 PIDs) → kein Cross-Container
/proc-Zugriff.
- **Netzwerk:** RQ erreicht Tolaria intern (`10.0.4.1:5173` → `{"ok":true}`) und
Forgejo (`10.0.4.1:3000`). Tolaria läuft in SEPARATEM Container auf VPS-Host.
- **C5-Caller:** KEIN Daemon, KEIN separater Container. `C5CPropagator` (SAVE) und
`DeleteExecutor` (DELETE) sind Library/CLI, die manuell im RQ-Container aufgerufen
werden (Hermes-Gateway-Prozess). C5-DB `/opt/data/c5c_live/c5a.db` liegt im RQ-Container.
- **ENV im RQ-Container:** `TOLARIA_SAVE_TOKEN` ABSENT, `TOLARIA_DELETE_TOKEN` ABSENT
(korrekt — RQ credential-free). Vorhandene Secrets: `FORGEJO_WRITE_TOKEN`,
`NOTION_API_KEY`, `TELEGRAM_BOT_TOKEN`, `HERMES_SESSION_KEY` (Namen, keine Werte).
### CAN_*_READ_SECRET — SAVE und DELETE (identisch, da gleiche Boundary)
| Akteur | SAVE | DELETE | Weg |
|---|---|---|---|
| **RQ/Hermes** (Container `06a4a73573d0`) | **NEIN** (tatsächlich) | **NEIN** (tatsächlich) | Kein Docker-Socket, kein Host-SSH, kein /proc-Cross, kein /opt/tolaria. ENV ABSENT. |
| **RQ/Hermes** (theoretisch via root) | JA (wenn Host-root) | JA (wenn Host-root) | Host-root kann Container-ENV via `docker inspect`/`docker exec` lesen. |
| **Rain/OpenClaw** (Alice 55163, Matt 54524) | **NEIN** (tatsächlich) | **NEIN** (tatsächlich) | Separate Container, kein Zugriff auf RQ-ENV oder Tolaria-Container. |
| **Anderer Container** (Tolaria selbst) | JA (eigener ENV) | JA (eigener ENV) | Tolaria-Container hält seine eigenen erwarteten Tokens. |
| **Docker-Admin** (VPS-Host) | JA | JA | `docker inspect`/`docker exec` auf Tolaria-Container. |
| **Host-Root** (VPS) | JA | JA | Liest Compose, .env, Container-ENV, Mounts. |
| **C5C Writer** (SAVE-Prozess) | JA (SAVE) | NEIN (kein DELETE) | Erhält nur SAVE-Token via injiziertem Client. |
| **DeleteExecutor** (DELETE-Prozess) | NEIN (kein SAVE) | JA (DELETE) | Erhält nur DELETE-Token via ENV. |
### Security-Boundary-Befund
- **Tolaria-Server** = eigene Boundary (separater Container, VPS-Host).
- **RQ/Hermes** = eigene Boundary (Container `06a4a73573d0`), aber **C5-Caller
(SAVE+DELETE) laufen INNERHALB dieser Boundary**.
- **CRITICAL:** SAVE- und DELETE-Credential würden, wenn beide injiziert würden,
im **selben Container** (`06a4a73573d0`) landen. Es gibt KEINE getrennte
Runtime-Boundary zwischen SAVE-Caller und DELETE-Caller.
- **Konsequenz für Phase 3:** Die Zielarchitektur "SAVE≠DELETE als getrennte
Boundaries" ist mit dem realen Deployment **NICHT vollständig erreichbar**, solange
beide Caller im selben RQ-Container laufen. Die Separation ist NUR logisch
(verschiedene ENV-Namen, verschiedene Klassen, verschiedene CLI-Commands), NICHT
technisch als getrennte Container/Prozesse.
---
## 3b. CREDENTIAL OWNERSHIP MODEL (Phase 3) — Zielarchitektur vs. Realität
### Zielarchitektur (verbindlich aus Freigabe)
- SAVE credential → Tolaria Server + C5C Writer
- DELETE credential → Tolaria Server + C5 DeleteExecutor
- RQ: KEIN SAVE, KEIN DELETE
- Hermes generisch: KEIN SAVE, KEIN DELETE
- Rain: KEIN SAVE, KEIN DELETE
- Kein Master-Token
- SAVE darf DELETE nicht autorisieren; DELETE darf SAVE nicht autorisieren
### Erreichbarkeit mit realem Deployment
**1) Server-seitige Auth (AUTH.2) — ERREICHBAR ✓**
- Tolaria-Server hält SAVE+DELETE-Tokens in seiner eigenen ENV (separater Container).
- RQ/Hermes/Rain haben KEINEN Zugriff auf den Tolaria-Container (kein Docker-Socket,
kein Host-SSH, kein /proc-Cross).
- Server-seitige Scope-Trennung (SAVE≠DELETE) ist im Code verifiziert (AUTH.2).
**2) Caller-seitige Credential-Separation — NICHT ERREICHBAR (HARD STOP-Kandidat) ✗**
- **Ursache:** C5C Writer (SAVE) und DeleteExecutor (DELETE) laufen im **selben
Container** wie RQ/Hermes (`06a4a73573d0`). Es gibt KEINE getrennte Runtime-Boundary.
- Wenn SAVE- und DELETE-Tokens in die ENV dieses Containers injiziert würden, hätte
RQ/Hermes (das im selben Container läuft, gleicher User `hermes`) **technischen
Zugriff auf BEIDE Tokens**.
- Das verletzt die Zielarchitektur "RQ: KEIN SAVE KEIN DELETE" und "Hermes: KEIN
SAVE KEIN DELETE" — unabhängig davon, ob RQ den Zugriff tatsächlich nutzt.
### HARD STOP — exakte Ursache
**Die Zielarchitektur "RQ/Hermes credential-free" ist mit dem realen Deployment
NICHT erreichbar, solange die C5-Caller (SAVE+DELETE) im selben Container wie
RQ/Hermes laufen und ihre Tokens in dessen ENV liegen.**
Notwendige Voraussetzung für AUTH.4 (Architektur-Änderung, NICHT in AUTH.3B):
- **Option A (empfohlen):** C5C Writer und DeleteExecutor in einen **separaten
Container/Prozess** mit eigener Boundary verschieben, der NICHT der RQ-Container
ist. RQ/Hermes ruft diesen Container nur über eine schmale Schnittstelle auf.
- **Option B:** Tokens über einen **separaten Secret-Store** injizieren, auf den nur
der C5-Caller-Prozess (nicht RQ/Hermes) Zugriff hat. Im selben Container mit
gleichem User ist das praktisch nicht durchsetzbar (RQ kann dieselbe Datei lesen).
**Ohne diese Änderung bleibt die Separation NUR logisch** (verschiedene ENV-Namen,
verschiedene Klassen, verschiedene CLI-Commands), NICHT technisch als getrennte
Security Boundaries. Das wird NICHT stillschweigend abgeschwächt — es wird als
HARD STOP für die volle Zielarchitektur dokumentiert.
### Was TROTZDEM sicher erreichbar ist (ohne Architektur-Änderung)
- **Server-seitige Auth (AUTH.2) aktivieren:** Tolaria-Server fail-closed, SAVE≠DELETE
Scope-Trennung. RQ bleibt credential-free (kein Zugriff auf Tolaria-Container).
- **SAVE-Aktivierung:** C5C Writer erhält SAVE-Token. ABER: da im RQ-Container,
hätte RQ technisch Zugriff → verletzt "RQ credential-free" für SAVE.
- **DELETE-Aktivierung:** DeleteExecutor erhält DELETE-Token. ABER: da im RQ-Container,
hätte RQ technisch Zugriff → verletzt "RQ credential-free" für DELETE.
### Fazit Phase 3
- **SERVER_AUTH_DEPLOYMENT:** ERREICHBAR (RQ bleibt credential-free ggü. Tolaria).
- **SAVE_ACTIVATION / DELETE_CREDENTIAL_ACTIVATION:** BLOCKED bis C5-Caller in
getrennte Boundary verschoben ODER Secret-Injection RQ-undurchdringbar ist.
- **Kein Master-Token:** ERREICHBAR (AUTH.2/AUTH.3A verifiziert, kein Master-Key).
- **SAVE≠DELETE Scope-Trennung:** Server-seitig ERREICHBAR; Caller-seitig nur logisch.
---
## 4b. SECRET STORAGE OPTIONS (Phase 4) — Bewertung
### Kontext: Was existiert im realen Stack?
- **Coolify-Suite** ist auf dem VPS installiert (infrastructure-handbook Z.52).
- **Exakte Tolaria-Provisionierung (Compose vs Coolify) ist aus RQ-Sicht UNKNOWN**
(kein Docker-Socket, kein Host-SSH). CURRENT_STATE Z.27: "UNKNOWN: Container-Name/
Image-Konfiguration, Env-Variablen, Mounts, Volumes — nur über Host-/Docker-Zugriff
ermittelbar, der nicht verfügbar ist."
- **Kein separater Secret-Store** (kein Vault/Consul/SOPS/age) im Stack nachweisbar.
- **Kein Docker-Secrets-Mechanismus** im Stack nachweisbar (kein Swarm).
- Tolaria-Server liest ENV via `process.env.TOLARIA_SAVE_TOKEN` (vite.config.ts).
### Optionen-Bewertung (SAVE und DELETE, identisch)
| Kriterium | A) Plain Compose ENV | B) .env-Datei | C) Docker Secrets | D) read-only Secret-Datei | E) Coolify Secret Mgmt | F) separater Secret-Store |
|---|---|---|---|---|---|---|
| SECURITY | Schwach (ENV in inspect) | Mittel (Datei, chmod) | Stark (nur im Container) | Mittel (Datei, chmod) | Stark (Coolify-UI) | Stark |
| VISIBILITY | `docker inspect` sichtbar | Datei, nicht inspect | Nicht in inspect | Datei, nicht inspect | Nicht in inspect | Nicht in inspect |
| DOCKER_INSPECT_EXPOSURE | **JA** | NEIN | NEIN | NEIN | NEIN | NEIN |
| HOST_EXPOSURE | JA (root liest) | JA (root liest) | JA (root liest) | JA (root liest) | JA (root liest) | JA (root liest) |
| RQ_EXPOSURE | JA (wenn im RQ-Container) | JA (wenn im RQ-Container) | NEIN (nur Ziel-Container) | JA (wenn im RQ-Container) | NEIN | NEIN |
| ROTATION | Restart nötig | Restart nötig | Restart nötig | Restart nötig | Restart nötig | Restart nötig |
| REVOCATION | ENV entfernen + Restart | Datei löschen + Restart | Secret entfernen + Restart | Datei löschen + Restart | ENV entfernen + Restart | Store-Update + Restart |
| RESTART_BEHAVIOR | ENV bleibt | Datei bleibt | Secret bleibt | Datei bleibt | ENV bleibt | Store bleibt |
| BACKUP_BEHAVIOR | ENV nicht in Backup | Datei evtl. in Backup | Secret nicht in Backup | Datei evtl. in Backup | ENV nicht in Backup | Store in Backup |
| OPERATIONAL_COMPLEXITY | Niedrig | Niedrig | Mittel (Swarm nötig) | Niedrig | Mittel | Hoch |
| COMPATIBILITY | Hoch (Vite liest ENV) | Hoch | Niedrig (kein Swarm) | Hoch (Datei lesen) | Mittel (Coolify vorhanden) | Niedrig (nicht vorhanden) |
| BLAST_RADIUS | Hoch (inspect) | Mittel | Niedrig | Mittel | Niedrig | Niedrig |
### Empfehlung für UNSEREN Stack
**Minimal sichere Variante: B) .env-Datei (oder D) read-only Secret-Datei) mit
strikten Permissions, injiziert in die Tolaria-Container-ENV.**
Begründung:
- **A (Plain Compose ENV) ist abzulehnen:** `docker inspect` würde die Tokenwerte
offenlegen (DOCKER_INSPECT_EXPOSURE=JA). Das verletzt die Anforderung "kein Secret
in docker inspect".
- **C (Docker Secrets) ist nicht kompatibel:** erfordert Docker Swarm, das im Stack
nicht nachweisbar ist.
- **F (separater Secret-Store) ist nicht vorhanden:** keine neue Infrastruktur
erfinden (Freigabe-Anforderung).
- **E (Coolify Secret Mgmt):** Coolify ist vorhanden, aber die exakte Tolaria-
Provisionierung ist UNKNOWN. Falls Tolaria über Coolify deployed wird, ist Coolify
Secret Management die natürliche Wahl (nicht in inspect, Rotation via UI).
- **B/D sind die robuste Default-Empfehlung:** `.env`-Datei mit `chmod 600`, vom
Compose/Coolify in die Container-ENV injiziert. Tokenwerte erscheinen NICHT in
`docker inspect` (nur der ENV-Name, nicht der Wert, wenn via `env_file` injiziert).
**WICHTIG (Phase 3-Kopplung):** Unabhängig von der Storage-Option bleibt der
HARD STOP bestehen: Solange die C5-Caller im RQ-Container laufen, würde jede
Injektion in dessen ENV RQ technischen Zugriff geben. Die Storage-Empfehlung gilt
daher primär für den **Tolaria-Server-Container** (eigene Boundary, RQ-fern).
---
## 5b. TOKEN GENERATION CONTRACT (Phase 5) — NUR DESIGN, KEINE echten Tokens
> ⚠️ Dies ist ein **Design-Vertrag**. Es wird KEIN echtes Token erzeugt, kein Wert
> angezeigt, nichts in ENV/Compose/Logs/Telegram/Repo geschrieben.
### Kryptographische Zufallsquelle
- **`/dev/urandom`** (Linux) oder `secrets.token_urlsafe()` (Python) — kryptographisch
sicher, nicht-deterministisch.
- NICHT: `random`-Modul, `time`-basiert, UUID (nicht krypto-sicher genug für Auth).
### Mindestentropie
- **≥ 256 Bit** (32 Bytes) pro Token. Empfehlung: 32 Bytes aus `/dev/urandom`,
base64url-kodiert → 43 Zeichen.
### Format / Länge
- **Format:** base64url (URL-sicher, kein `+`/`/`/`=`), 43 Zeichen bei 32 Bytes.
- **Länge:** 43 Zeichen (256 Bit Entropie).
- **Keine semantischen Token-Präfixe** (kein `save-`, `delete-`, `tolaria-` im Wert) —
Präfixe wären unnötige Information und könnten Scope-Leaks erzeugen. Der Scope wird
durch die ENV-Variable bestimmt, nicht durch den Tokenwert.
### Unabhängigkeit
- **SAVE und DELETE unabhängig generiert** (zwei separate `/dev/urandom`-Ziehungen).
- **Niemals gleicher Wert** (bei 256 Bit Entropie kollisionsfrei).
- **Keine Ableitung SAVE→DELETE** (kein Hash/Substring/Transformation).
- **Kein Master-Key** (kein gemeinsamer Seed, kein hierarchisches Schema).
### Nicht-Speicherung / Nicht-Ausgabe
- **Keine Shell-History** (kein `echo $TOKEN`, kein Token in interaktiven Befehlen).
- **Keine Ausgabe in Telegram / Chat / Logs / FINAL REPORT.**
- Token wird NUR in die Ziel-ENV/Secret-Datei geschrieben (Tolaria-Server-Container),
nie in Git, nie in Reports, nie in Test-Fixtures.
### Wer die Tokens bei AUTH.4 erzeugen darf
- **Nur Christian (Mensch)** — manuell auf dem VPS-Host, via `/dev/urandom` oder
`openssl rand -base64 32`, direkt in die Secret-Datei/Coolify-ENV geschrieben.
- **NICHT RQ/Hermes/Rain** — kein Agent erzeugt oder sieht die echten Tokens.
- Erzeugung erfolgt im Rahmen der AUTH.4-Deployment-Sequenz (Phase 9), nicht in
AUTH.3B.
---
## 6b. SERVER INJECTION CONTRACT (Phase 6) — AUTH.2 im Code verifiziert
### Exakte ENV-Namen (im Code verifiziert, `vite.config.ts` Z.35-39)
- `TOLARIA_VAULT_ROOT` → Default `/app/vault`
- `TOLARIA_SAVE_TOKEN``saveToken` (Scope SAVE)
- `TOLARIA_DELETE_TOKEN``deleteToken` (Scope DELETE)
### Verhalten (im Code verifiziert, `vault-write-guard.ts` Z.112-131)
| Feld | Wert |
|---|---|
| SERVER_SAVE_ENV | `TOLARIA_SAVE_TOKEN` |
| SERVER_DELETE_ENV | `TOLARIA_DELETE_TOKEN` |
| MISSING_SAVE_BEHAVIOR | `expected.length === 0` → 401 "Write scope not configured" (fail-closed) |
| MISSING_DELETE_BEHAVIOR | dito (fail-closed) |
| EMPTY_TOKEN_BEHAVIOR | `|| ''` → leeres expected → 401 (fail-closed) |
| WRONG_SCOPE_BEHAVIOR | `constantTimeEqual(extracted, expected)` → falscher Scope → 401 "Invalid credentials" |
### Fail-closed bestätigt
- **Kein "Auth disabled because ENV missing".** Fehlende/leere Token-Konfiguration
→ mutierende Endpoints bleiben DENIED (401).
- Scope-Trennung: SAVE-Token autorisiert NUR SAVE; DELETE-Token NUR DELETE.
- Const-time-Vergleich (`constantTimeEqual`) gegen Timing-Angriffe.
- Auth-before-Mutation: bei Auth-Fehler wird Response gesendet, KEINE Mutation.
### Fazit Phase 6
- **SERVER_FAIL_CLOSED_STATUS = ENFORCED** (im Code verifiziert).
- Server erwartet exakt `TOLARIA_SAVE_TOKEN` + `TOLARIA_DELETE_TOKEN`.
- Kein Master-Token, keine Scope-Abschwächung.
---
## 7b. CALLER INJECTION CONTRACT (Phase 7) — AUTH.3A im Code verifiziert
### ENV-Contract (im Code verifiziert, `rq_c5c.py` Z.100-101)
- `ENV_TOLARIA_SAVE_TOKEN = "TOLARIA_SAVE_TOKEN"`
- `ENV_TOLARIA_DELETE_TOKEN = "TOLARIA_DELETE_TOKEN"`
### Credential-Verwendung (im Code verifiziert)
| Caller | ENV | Verwendung | Scope |
|---|---|---|---|
| C5C Writer (`C5CPropagator`) | `TOLARIA_SAVE_TOKEN` | `write()` Z.322: `self._require_token(self.save_token, "SAVE")` | SAVE |
| DeleteExecutor | `TOLARIA_DELETE_TOKEN` | `delete()` Z.352: `self._require_token(self.delete_token, "DELETE")` | DELETE |
| read/list | — | KEIN Credential | READ |
### Verifizierte Invarianten
- **SAVE-Prozess bekommt DELETE nicht:** `write()` nutzt NUR `self.save_token`.
- **DELETE-Prozess bekommt SAVE nicht:** `delete()` nutzt NUR `self.delete_token`.
- **read/list brauchen nichts:** `read()`/`list()` senden kein Bearer.
- **fehlendes Token => kein HTTP Write:** `_require_token` (Z.300-313) bricht lokal ab
(RC_AUTH_FAILURE), HTTP wird NICHT aufgerufen (fail-closed, AUTH.1 §4).
- **Retry behält Credential:** Client-Instanz hält Token über Retries hinweg.
- **Replay behält Credential:** `c5-delete-replay` nutzt denselben `_delete_executor`.
- **Token wird nicht persistiert:** kein Token in DB/Logs/Reports (AUTH.3A verifiziert).
### KRITISCHER BEFUND (Runtime-Separation)
- **Sind C5C Writer und DeleteExecutor heute getrennte Runtime-Prozesse/Boundaries?
NEIN.**
- Beide laufen im **selben Container** (`06a4a73573d0`, RQ/Hermes) und werden als
Library/CLI im selben Hermes-Gateway-Prozess aufgerufen.
- **Konsequenz:** Die Credential-Separation ist NUR logisch (verschiedene ENV-Namen,
verschiedene Klassen, verschiedene CLI-Commands). Wenn beide Tokens in die ENV
dieses Containers injiziert würden, hätte RQ/Hermes technischen Zugriff auf BEIDE.
- **Das wird NICHT als technische Separation behauptet.** Es ist explizit dokumentiert:
CREDENTIAL_SEPARATION_STATUS = LOGICAL_ONLY (nicht technisch).
### Fazit Phase 7
- **CALLER_FAIL_CLOSED_STATUS = ENFORCED** (im Code verifiziert).
- **CREDENTIAL_SEPARATION_STATUS = LOGICAL_ONLY** (nicht technisch, da gleiche Boundary).
- Least Privilege im Code korrekt; Runtime-Separation fehlt (HARD STOP aus Phase 3).
---
## 8b. HUMAN APPROVAL INTERACTION (Phase 8) — AUTH4_WITH_OPEN_APPROVAL_GAP
### Kontext
- **HUMAN_APPROVAL_AUTHENTICITY_GAP = OPEN** (aus AUTH.3A, NICHT repariert).
Die DELETE-Approval erfüllt Invarianten AN, aber die Provenance ist nicht
kryptographisch an Christian gebunden (frei wählbares CLI-Argument
`--approved-by`, `rq_c5_cli.py` Z.576).
- **DELETE-Execution (AUTH.3A, `rq_c5_delete.py` Z.192-251):** 4 Pre-Gates:
1. ObjectChange existiert + operation == DELETE
2. Commit-Status == ST_DELETE_APPROVED
3. Approval existiert, APPROVED, passt exakt (Commit/Objekt/Pfad) ← **Human Gate**
4. Pre-Delete-Drift-Check (Ziel existiert noch)
### Wie interagiert das DELETE-Token mit dem Human Gate?
- Das DELETE-Token (AUTH.2/AUTH.3A) ist eine **zusätzliche, unabhängige Bedingung**
auf Transport-Ebene: `delete()` sendet Bearer DELETE-Token; Server verweigert ohne
gültiges Token (401).
- Das Human Approval Gate (Gate 3) ist eine **separate Bedingung auf
Anwendungs-Ebene**: `execute()` verweigert ohne persistierte, exakt passende
Approval.
- **DELETE ist nur möglich, wenn BEIDES erfüllt ist:**
A) gültiges DELETE-Credential (Server-Auth) UND
B) bestehendes C5 Human Approval Gate (Gate 3).
### Verschärft AUTH.4 das Authenticity-Gap?
- **NEIN.** AUTH.4 fügt eine zusätzliche Hürde (DELETE-Token) hinzu, die das
bestehende Gap NICHT verschärft, sondern die DELETE-Ausführung zusätzlich
absichert.
- Das Gap (Provenance nicht kryptographisch an Christian gebunden) bleibt bestehen,
wird aber durch das DELETE-Token NICHT schwächer. Ein Angreifer, der das
Authenticity-Gap ausnutzt (frei wählbares `--approved-by`), bräuchte ZUSÄTZLICH
das DELETE-Token, um tatsächlich zu löschen.
- **Kein Risiko, dass durch das DELETE-Token ein schwächeres Human Gate akzeptiert
wird.** Das Human Gate (Gate 3) bleibt unverändert ENFORCED.
### Klassifikation
**AUTH4_WITH_OPEN_APPROVAL_GAP = SAFE_FOR_SERVER_AUTH_ONLY**
Begründung:
- Die Einführung des DELETE-Tokens (Server-Auth) verschärft das Authenticity-Gap
NICHT — es fügt eine unabhängige, zusätzliche Hürde hinzu.
- **ABER:** DELETE_OPERATION_ACTIVATION (tatsächliches Löschen) sollte erst nach
Schließung des Gaps freigegeben werden, um das Risiko eines kombinierten Angriffs
(Gap + gestohlenes Token) zu minimieren.
- **SAFE_FOR_SERVER_AUTH_ONLY** bedeutet: Server-seitige Auth (AUTH.2) kann sicher
aktiviert werden, ohne das Gap zu verschärfen. Die DELETE-Operation selbst bleibt
bis zur Gap-Schließung zurückhaltend zu behandeln (siehe Phase 14 GO/NO-GO).
---
## 9b. DEPLOYMENT SEQUENCE DESIGN (Phase 9) — AUTH.4 atomar, KEIN Big-Bang
> ⚠️ NUR DESIGN. KEINE AUSFÜHRUNG in AUTH.3B.
### Kernfrage: Caller zuerst vs Server zuerst?
**Server zuerst (AUTH.2 aktivieren), dann Caller (AUTH.3A).**
Begründung (kein Zeitfenster mit ungeschützten Writes):
- Wenn der **Caller zuerst** das SAVE-Token erhält, aber der **Server noch ohne
Auth** läuft, dann sendet der Caller zwar ein Bearer-Token, aber der Server
ignoriert es (Produktion läuft aktuell OHNE AUTH.2) → **Writes bleiben
ungeschützt** (ungültiges Zeitfenster).
- Wenn der **Server zuerst** AUTH.2 aktiviert (fail-closed), dann verweigert er
ALLE Writes ohne gültiges Token. In diesem Moment haben die Caller noch KEIN
Token → **legitime C5-Writes fallen kurzzeitig aus** (kontrolliertes, kurzes
Fenster). Das ist akzeptabel, weil es FAIL-CLOSED ist (kein ungeschützter Write).
- **Kein Zeitfenster, in dem Writes ungeschützt bleiben:** Server-Auth zuerst
schließt das Loch sofort; Caller-Token werden danach injiziert.
- **Kein Zeitfenster, in dem DELETE ohne Human Gate möglich wird:** DELETE-Token
wird erst injiziert, wenn das Human Gate (Gate 3) bereits ENFORCED ist (AUTH.3A
Code ist deployed). DELETE bleibt doppelt abgesichert.
### Atomare AUTH.4-Reihenfolge (jeder Schritt einzeln, mit Gate)
| # | Schritt | Aktion | Gate/Verifikation |
|---|---|---|---|
| 1 | **PRECHECK** | Produktiver Source == Forgejo `edcb4902`; AUTH.2/AUTH.3A Code deployed; kein Token in ENV | `git diff` leer, ENV-Namen ABSENT |
| 2 | **BACKUP** | Vault + Compose + aktuelle ENV-Namen sichern (KEINE Tokenwerte) | Backup-Datei existiert |
| 3 | **TOKEN_GENERATION** | Christian erzeugt SAVE+DELETE-Token (Phase 5 Contract), schreibt in Secret-Datei/Coolify | Token in Ziel-ENV, NICHT in Git/Logs |
| 4 | **SERVER_SECRET_INJECTION** | `TOLARIA_SAVE_TOKEN` + `TOLARIA_DELETE_TOKEN` in Tolaria-Container-ENV (via .env/Coolify) | ENV-Namen PRESENT (Werte nicht prüfen) |
| 5 | **CALLER_SECRET_INJECTION** | SAVE-Token an C5C Writer, DELETE-Token an DeleteExecutor (NUR nach Boundary-Fix, Phase 3) | Getrennte Boundaries ODER HARD STOP |
| 6 | **CODE_DEPLOY** | AUTH.2-Source (edcb4902) in Produktion deployen (Container-Restart) | Server startet, Read funktioniert |
| 7 | **SERVER_RESTART** | Tolaria-Container neu starten (liest neue ENV) | Healthcheck OK |
| 8 | **READ_SMOKE** | `GET /api/vault/ping`, `/list`, `/content` ohne Token → 200 | Read funktioniert |
| 9 | **UNAUTH_WRITE_NEGATIVE_TEST** | `POST /save` ohne Token → 401 (fail-closed) | Write DENIED |
| 10 | **WRONG_SCOPE_NEGATIVE_TEST** | `POST /save` mit DELETE-Token → 401; `POST /delete` mit SAVE-Token → 401 | Scope-Trennung |
| 11 | **AUTHORIZED_SAVE_TEST** | `POST /save` mit SAVE-Token → 200 (isolierte Fixture) | SAVE funktioniert |
| 12 | **DELETE_GATE_NEGATIVE_TEST** | `POST /delete` mit DELETE-Token aber OHNE Human Approval → DENIED | Human Gate ENFORCED |
| 13 | **AUTHORIZED_DELETE_TEST** | `POST /delete` mit DELETE-Token + Human Approval → 200 (NUR falls freigabefähig) | DELETE funktioniert |
| 14 | **POST_DEPLOY_CHECK** | Read + Write + Scope + kein Token in Logs/inspect | Alle Checks grün |
| 15 | **FRESH_CHECKER** | Unabhängiger Checker verifiziert alle Gates | PASS |
### Wichtige Design-Entscheidungen
- **Kein Big-Bang:** Jeder Schritt hat ein eigenes Gate; bei Fehler → Rollback
(Phase 10), nicht weiter.
- **Schritt 5 (CALLER_SECRET_INJECTION) ist BLOCKED** bis die Boundary-Frage aus
Phase 3 gelöst ist (C5-Caller in getrennten Container ODER RQ-undurchdringbare
Injektion). Ohne das bleibt RQ credential-free NICHT erreichbar.
- **Schritt 13 (AUTHORIZED_DELETE_TEST) ist CONDITIONALLY_READY** — nur falls
DELETE-Operation freigabefähig ist (siehe Phase 14, Gap-Berücksichtigung).
- **Server zuerst** minimiert das ungeschützte Write-Fenster auf null.
---
## 10b. ROLLBACK DESIGN (Phase 10) — FAIL CLOSED, Write-Degraded-Mode
> ⚠️ NUR DESIGN. KEINE AUSFÜHRUNG in AUTH.3B.
### Grundprinzip
- **FAIL CLOSED bevorzugen.** Bei Credential-/Auth-Problem NICHT automatisch auf
unauthentifizierte Writes zurückrollen.
- **Ein Rollback darf NICHT bedeuten:** "Auth ausschalten und unauthentifizierte
Writes wieder erlauben", wenn dadurch die bereits identifizierte CRITICAL Gap
(Produktion läuft aktuell OHNE Auth) wieder geöffnet wird.
- **Bevorzugter Security-Rollback = WRITE_DEGRADED_MODE:**
- **READ AVAILABLE** (Read-Endpunkte funktionieren weiter)
- **WRITE DISABLED** (mutierende Endpunkte bleiben DENIED)
### Exakte Rollback-Pfade
| Fall | Symptom | Rollback-Aktion | Security-Position |
|---|---|---|---|
| **A) Server startet nicht** | Container-Crash nach AUTH.2-Deploy | Source auf `b5a5e4bd` (Basisstand) zurück; ENV-Tokens entfernen; Restart | FAIL CLOSED (kein Write ohne Auth) |
| **B) Read-Endpunkte kaputt** | `/ping`, `/list`, `/content` 500 | Source auf `b5a5e4bd` zurück; ENV-Tokens entfernen; Restart | FAIL CLOSED |
| **C) C5C SAVE schlägt fehl** | SAVE-Writes 401/500 | SAVE-Token prüfen/erneuern; NICHT Auth deaktivieren | WRITE_DEGRADED (SAVE disabled, READ available) |
| **D) DELETE-Caller schlägt fehl** | DELETE-Writes 401/500 | DELETE-Token prüfen/erneuern; NICHT Human Gate deaktivieren | WRITE_DEGRADED (DELETE disabled, READ available) |
| **E) Token falsch injiziert** | Wrong-Scope 401 | Token korrekt injizieren; NICHT Auth deaktivieren | FAIL CLOSED |
| **F) Token-Leak vermutet** | Token in Logs/inspect/Repo | Token SOFORT rotieren (Phase 11); NICHT Auth deaktivieren | FAIL CLOSED |
| **G) Falscher Scope** | SAVE-Token autorisiert DELETE (o. umgekehrt) | Scope-Trennung prüfen; Token neu generieren | FAIL CLOSED |
| **H) Regression nach Restart** | Read/Write unerwartet | Source auf `b5a5e4bd` zurück; ENV-Tokens entfernen; Restart | FAIL CLOSED |
### WRITE_DEGRADED_MODE (bevorzugter Security-Rollback)
- **READ AVAILABLE:** `/api/vault/ping`, `/list`, `/content`, `/all-content`,
`/entry`, `/search` funktionieren (kein Token nötig).
- **WRITE DISABLED:** `/api/vault/save`, `/rename`, `/rename-filename`, `/delete`,
`/command` bleiben DENIED (401) — fail-closed, weil Token-Konfiguration entfernt
oder ungültig ist.
- **Wie erreicht:** ENV-Tokens aus Tolaria-Container entfernen (oder auf ungültig
setzen) + Restart. Server bleibt fail-closed (leeres expected → 401).
- **WICHTIG:** Das ist KEIN "Auth ausschalten". Es ist "Auth auf DENIED-ALL
setzen" — Writes sind blockiert, nicht ungeschützt.
### Warum NICHT auf unauthentifizierte Writes zurückrollen
- Die Produktion läuft aktuell OHNE Auth (CRITICAL Gap, verifiziert in Phase 1).
- Ein Rollback auf "Auth aus" würde dieses Gap wieder öffnen und Writes ungeschützt
lassen.
- WRITE_DEGRADED_MODE hält Writes geschützt (DENIED), während Read verfügbar bleibt
— das ist die sichere Zwischenposition bis zur Fehlerbehebung.
---
## 11b. TOKEN ROTATION / REVOCATION (Phase 11) — SAVE/DELETE getrennt
> ⚠️ NUR DESIGN. KEINE AUSFÜHRUNG in AUTH.3B.
### Unterstützt AUTH.2 nur einen Token pro Scope?
- **JA.** `VaultAuthConfig` (`vite.config.ts` Z.36-39) hält `saveToken` und
`deleteToken` als **einzelne Strings** (kein Array, keine Multi-Token-Unterstützung).
- `authorizeVaultWrite` (Z.120-126) vergleicht gegen genau EINEN erwarteten Token
pro Scope.
- **Konsequenz:** Zero-downtime-Rotation (zwei gültige Tokens gleichzeitig) ist
mit AUTH.2 NICHT möglich. Rotation erfordert ein kurzes Write-Downtime-Fenster.
### SAVE-Rotation (minimales Write-Downtime-Fenster)
1. Neues SAVE-Token erzeugen (Phase 5 Contract).
2. Neues Token in Tolaria-Container-ENV schreiben (ersetzt altes).
3. Tolaria-Container neu starten (liest neues Token).
4. C5C Writer erhält neues SAVE-Token (nach Boundary-Fix, Phase 3).
5. Altes SAVE-Token ist sofort ungültig (Server vergleicht nur gegen neues).
6. **Write-Downtime-Fenster:** zwischen Schritt 3 und 4 sendet C5C Writer noch
altes Token → 401. Fenster = Zeit bis C5C Writer aktualisiert ist.
7. **Verifikation:** `POST /save` mit neuem Token → 200; mit altem → 401.
### DELETE-Rotation (analog, getrennt)
1. Neues DELETE-Token erzeugen.
2. In Tolaria-Container-ENV schreiben (ersetzt altes).
3. Tolaria-Container neu starten.
4. DeleteExecutor erhält neues DELETE-Token.
5. Altes DELETE-Token sofort ungültig.
6. **Verifikation:** `POST /delete` mit neuem Token + Human Approval → 200; mit
altem → 401.
### Laufende Retries / queued Jobs
- **Retries:** Ein laufender Retry mit altem Token schlägt nach Rotation fehl (401).
Der Retry muss das neue Token verwenden (Client-Instanz neu konstruieren).
- **Queued Jobs:** C5-Queue-Einträge, die vor Rotation geplant wurden, verwenden
beim Ausführen das aktuelle Token (Client wird zur Laufzeit konstruiert). Nach
Rotation nutzen sie das neue Token.
- **Empfehlung:** Rotation in einem Wartungsfenster durchführen, wenn keine
kritischen Writes anstehen.
### DELETE-Revocation (unabhängig von SAVE)
- **DELETE-Revocation muss unabhängig von SAVE möglich sein:** Ja — DELETE-Token
ist eine separate ENV-Variable (`TOLARIA_DELETE_TOKEN`). Entfernen/ersetzen
dieser Variable + Restart widerruft DELETE, ohne SAVE zu beeinflussen.
- **SAVE-Revocation analog:** `TOLARIA_SAVE_TOKEN` entfernen/ersetzen + Restart
widerruft SAVE, ohne DELETE zu beeinflussen.
- **Revocation = sofort wirksam** nach Container-Restart (Server liest ENV beim
Start; kein Cache).
### Welche Komponenten müssen restart/reload?
- **Tolaria-Server-Container:** Restart nötig (liest ENV beim Start).
- **C5C Writer / DeleteExecutor:** Client-Instanz neu konstruieren (neues Token).
Kein Container-Restart nötig, wenn Token via ENV/Config injiziert wird und der
Prozess neu gestartet wird.
### Wie wird Rotation verifiziert?
- **Positiv:** Write mit neuem Token → 200.
- **Negativ:** Write mit altem Token → 401 (fail-closed).
- **Scope:** SAVE-Token autorisiert nicht DELETE und umgekehrt.
- **Kein Token-Leak:** neues Token nicht in Logs/inspect/Repo.
---
## 12b. BACKUP / RECOVERY (Phase 12) — Secret-Handling
> ⚠️ NUR DESIGN. KEINE Backup-Konfiguration ändern in AUTH.3B.
### Werden Secret-Werte in normalen Backups erfasst?
- **NEIN.** Das Tolaria-Backup-Skript (`/opt/data/bin/tolaria_backup.py`) sichert
NUR Vault-Content (`.md`-Dateien) und App-Source über die read-only API
(`/api/vault/list`, `/api/vault/content`). Es sichert KEINE ENV-Variablen,
KEINE Compose-Datei, KEINE Secret-Dateien.
- **Sollten sie enthalten sein?** NEIN. Secrets gehören NICHT in Backups. Sie
werden separat (außerhalb des Backup-Pfads) verwaltet und bei Restore neu
injiziert.
### Wie wird Disaster Recovery durchgeführt?
1. Vault-Content aus Backup wiederherstellen (via `tolaria_backup.py` oder
Vault-Git-Restore).
2. App-Source aus Forgejo (`edcb4902`) neu deployen (rebuildbar, Forgejo=SoT).
3. **Secrets NEU injizieren** (nicht aus Backup): Christian erzeugt frische
SAVE/DELETE-Tokens (Phase 5 Contract) und schreibt sie in die Container-ENV.
### Müssen Tokens nach Restore rotiert werden?
- **JA, zwingend.** Nach einem Disaster Recovery werden frische Tokens erzeugt
(nicht die alten aus dem Backup). Das verhindert, dass alte, möglicherweise
kompromittierte Tokens weiterleben.
### Können alte Backups alte gültige Tokens enthalten?
- **NEIN** (aktuell): Das Backup-Skript sichert keine Secrets. Aber falls in
Zukunft ein Backup die Compose-Datei oder ENV mit aufnimmt, könnten alte Tokens
enthalten sein.
### Wie verhindern wir Secret-Restore aus veraltetem Backup?
- **Backup-Skript sichert keine Secrets** (verifiziert, Z.17 "Keine Secrets im
Manifest/Log").
- **Restore-Prozedur injiziert IMMER frische Tokens** (nie aus Backup).
- **Empfehlung:** Falls ein Backup je Compose/ENV aufnimmt, Secrets vor dem
Backup strikt ausschließen (`.gitignore`-artige Exklusion) und nach Restore
rotieren.
### Empfehlung (dokumentiert, KEINE Änderung)
- Secrets NIE in Backups aufnehmen.
- Nach jedem Restore frische Tokens erzeugen.
- Backup-Manifest/Logs secret-frei halten (bereits der Fall).
---
## 13b. ISOLATED DEPLOYMENT TEST PLAN (Phase 13) — AUTH.4
> ⚠️ NUR TESTPLAN. KEINE produktiven Mutationsprobes in AUTH.3B.
> Alle Tests werden in AUTH.4 in isolierter Umgebung (Fixture-Vault, synthetische
> Tokens) ausgeführt, NICHT gegen Produktion.
| ID | Test | Erwartung | Typ |
|---|---|---|---|
| T1 | READ ohne Token | `/ping`, `/list`, `/content` → 200 | Positiv |
| T2 | SAVE ohne Token | `POST /save` → 401 (fail-closed) | Negativ |
| T3 | SAVE wrong token | `POST /save` mit falschem Token → 401 | Negativ |
| T4 | SAVE DELETE-token | `POST /save` mit DELETE-Token → 401 (Scope) | Negativ |
| T5 | SAVE korrektes SAVE-token | `POST /save` mit SAVE-Token → 200 | Positiv |
| T6 | DELETE ohne Token | `POST /delete` → 401 (fail-closed) | Negativ |
| T7 | DELETE wrong token | `POST /delete` mit falschem Token → 401 | Negativ |
| T8 | DELETE SAVE-token | `POST /delete` mit SAVE-Token → 401 (Scope) | Negativ |
| T9 | DELETE korrektes Token, ohne Human Approval | → DENIED (Gate 3) | Negativ |
| T10 | DELETE Approval, aber ohne Token | → 401 (fail-closed) | Negativ |
| T11 | DELETE Token + Approval | → 200 NUR falls ausdrücklich freigegeben | Positiv |
| T12 | missing server ENV | Server startet, Writes DENIED (fail-closed) | Negativ |
| T13 | missing caller ENV | Caller bricht vor HTTP ab (fail-closed) | Negativ |
| T14 | Restart erhält Security State | Nach Restart weiterhin fail-closed | Positiv |
| T15 | kein Token in Logs | Logs secret-frei | Negativ |
| T16 | kein Token in inspect | `docker inspect` secret-frei (soweit Architektur) | Negativ |
| T17 | kein Token in Repo | Git-History secret-frei | Negativ |
| T18 | kein Token im Vault | Vault-Content secret-frei | Negativ |
| T19 | kein Token in DB | C5-DB secret-frei | Negativ |
| T20 | Retry sicher | Retry behält Credential, kein Leak | Positiv |
| T21 | Replay sicher | Replay behält Credential, kein Leak | Positiv |
| T22 | READ während Write-Degraded-Mode | Read 200, Write DENIED | Positiv |
| T23 | SAVE/DELETE getrennt widerrufbar | Revocation eines Scopes beeinflusst anderen nicht | Positiv |
| T24 | Path traversal weiterhin denied | `../` → 401/400 | Negativ |
| T25 | Symlink escape weiterhin denied | Symlink → 401/400 | Negativ |
| T26 | `/api/vault/command` kann Auth nicht umgehen | Command ohne Token → 401 | Negativ |
### Test-Ausführungsregeln (AUTH.4)
- Alle Tests in isolierter Umgebung (Fixture-Vault, synthetische Tokens
`auth4-...`), NICHT gegen Produktion.
- T11 (DELETE Token + Approval) NUR wenn DELETE-Operation freigabefähig ist
(siehe Phase 14).
- Keine produktiven Mutationsprobes in AUTH.3B.
---
## 14b. AUTH.4 GO/NO-GO MATRIX (Phase 14)
> ⚠️ NUR BEWERTUNG. KEINE Aktivierung in AUTH.3B.
| Punkt | Status | Konkrete Blocker |
|---|---|---|
| **SERVER_AUTH_DEPLOYMENT** | **CONDITIONALLY_READY** | AUTH.2-Code (edcb4902) ist fertig + getestet (70/70, Fresh Checker 17/17). Blocker: produktiver Source muss == Forgejo `edcb4902` sein; ENV-Tokens müssen injiziert werden; Container-Restart nötig. Kein Code-Blocker. |
| **SAVE_ACTIVATION** | **CONDITIONALLY_READY** | AUTH.3A-Code (a11c1bb) fertig + getestet (19/19, 318/318). Blocker: SAVE-Token muss an C5C Writer injiziert werden; **Boundary-Frage (Phase 3) muss gelöst sein** — sonst hätte RQ Zugriff auf SAVE-Token. |
| **DELETE_CREDENTIAL_ACTIVATION** | **BLOCKED** | DELETE-Token-Injektion an DeleteExecutor erfordert getrennte Boundary (Phase 3). Aktuell laufen SAVE+DELETE im selben Container → RQ hätte Zugriff auf BEIDE Tokens. **HARD STOP bis Boundary-Fix.** |
| **DELETE_OPERATION_ACTIVATION** | **BLOCKED** | Zusätzlich zum Boundary-Fix: HUMAN_APPROVAL_AUTHENTICITY_GAP ist OPEN. DELETE-Operation erst nach Gap-Schließung freigeben (kombinierter Angriff Gap+Token). |
| **HERMES_READ_ONLY_AUTONOMY** | **CONDITIONALLY_READY** | Read-Endpunkte brauchen kein Token; RQ bleibt credential-free für Read. Blocker: keine (Read ist bereits möglich). |
| **HERMES_BOUNDED_AUTONOMY** | **BLOCKED** | Erfordert SAVE/DELETE-Aktivierung, die wiederum Boundary-Fix + Gap-Schließung voraussetzt. |
### Kern-Blocker (zusammenfassend)
1. **CREDENTIAL_SEPARATION (Phase 3):** SAVE+DELETE laufen im selben Container
(`06a4a73573d0`). RQ credential-free ist technisch NICHT erreichbar, solange
beide Tokens in die ENV dieses Containers injiziert würden. → **HARD STOP für
DELETE_CREDENTIAL_ACTIVATION.**
2. **HUMAN_APPROVAL_AUTHENTICITY_GAP (OPEN):** DELETE-Operation erst nach
Gap-Schließung. → **BLOCKED für DELETE_OPERATION_ACTIVATION.**
3. **SERVER_AUTH_DEPLOYMENT** und **SAVE_ACTIVATION** sind CONDITIONALLY_READY
(Code fertig), aber SAVE_ACTIVATION hängt an der Boundary-Frage.
### Empfohlene Reihenfolge (AUTH.4, nach Freigabe)
1. SERVER_AUTH_DEPLOYMENT (AUTH.2 aktivieren) — sicher, verschärft Gap nicht.
2. Boundary-Fix (C5-Caller in getrennten Container ODER RQ-undurchdringbare
Injektion) — VORAUSSETZUNG für SAVE/DELETE-Credential-Aktivierung.
3. SAVE_ACTIVATION (nach Boundary-Fix).
4. Gap-Schließung (separate Freigabe).
5. DELETE_CREDENTIAL_ACTIVATION + DELETE_OPERATION_ACTIVATION (nach 2+4).
---
## 15. INCIDENT (AUTH.3B, 2026-08-27)
**INCIDENT_OCCURRED = TRUE** (während Phase 1 Discovery)
- Versehentlicher mutierender POST `POST /api/vault/save` gegen Produktion
(`_auth3b_probe.md`, Inhalt "x") — Verstoß gegen die verbindliche Incident-Regel
(NO MUTATING HTTP METHOD PROBES).
- **INCIDENT_RECOVERED = TRUE** — Datei sofort via `POST /api/vault/delete` entfernt,
Read-Back bestätigt: Datei existiert nicht mehr.
- **PERMANENT_DAMAGE = NONE VERIFIED**
- **ROOT_CAUSE = unsafe mutating endpoint probe during read-only discovery**
- **BESTÄTIGT:** Produktion läuft OHNE AUTH.2 (save ohne Token → 200, kein 401).
- **LEHRE:** Keine weiteren mutierenden POSTs. Nur read-only GET/Code-Reading.