trading-system-docs/tolaria/tolaria-write-auth/AUTH3E_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

32 KiB
Raw Blame History

AUTH.3E — EXECUTOR COMMAND CHANNEL + RUNTIME BOUNDARY CONTRACT

Status: DESIGN + CODE + ISOLATED TESTS + REVIEW ONLY (KEIN produktives Deployment) Datum: 2026-08-27 Mission Type: SECURITY DESIGN + CODE + ISOLATED TESTS + REVIEW ONLY Baseline: AUTH.1 (40bbc40) + AUTH.2 (edcb4902) + AUTH.3A (a11c1bb) + AUTH.3B + AUTH.3C + AUTH.3D (9c8d239) = COMPLETE

⚠️ Dies ist ein Design-/Code-/Test-Dokument. Es wird KEIN produktiver Umbau ausgeführt, kein Container erstellt, keine Compose/Network/Volume/DB geändert, kein echtes Credential erzeugt, keine ENV geändert, kein Deployment, keine produktive Tolaria-Mutationsprobe, keine Hermes-Autonomie. Bei Zweifel: FAIL CLOSED + HARD STOP.


1. CURRENT RUNTIME DISCOVERY (Phase 2) — rekonstruiert (READ-ONLY)

Keine Annahmen. UNKNOWN bleibt UNKNOWN. Alle Fakten aus Code-Reading verifiziert.

1a. SAVE-Pfad: RQ/Hermes → C5 CLI → C5CPropagator → TolariaClient.write()

RQ/Hermes (Container 06a4a73573d0, User hermes uid 10010)
  │
  ├─ [Entry Point] rq_c5_cli.py (argparse CLI, manuell aufgerufen)
  │     KEIN CLI-Command führt C5CPropagator mit save_token aus (verifiziert)
  │
  ├─ [Library] rq_c5c.py  C5CPropagator (Z.497)
  │     __init__(store, reader, client=None, ...)  → client = client or TolariaClient()
  │     Default: credential-frei (kein save_token)
  │     write() → self.client.write(vault_path, after_content)  (Z.638)
  │
  └─ [Library] rq_c5c.py  TolariaClient.write() (Z.322)
        token = self._require_token(self.save_token, "SAVE")   # fail-closed
        → POST /api/vault/save  (Bearer save_token)

Boundary-Fakten (SAVE):

  • Process Boundary: C5CPropagator läuft im RQ/Hermes-Prozess (kein separater Prozess).
  • Container Boundary: RQ-Container 06a4a73573d0 (User hermes, uid 10010, gid 10000).
  • Filesystem Boundary: /opt/data (RQ-Container-Dateisystem); C5-DB /opt/data/c5c_live/c5a.db.
  • ENV Boundary: TOLARIA_SAVE_TOKEN ABSENT im RQ-Container (verifiziert). C5A_DB steuert DB-Pfad.
  • Network Boundary: Tolaria 187.124.31.123:5173 (auch 10.0.4.1/10.0.9.1); Forgejo 10.0.4.1:3000/10.0.9.1:3000.
  • DB-Zugriff: C5AStore → SQLite c5a.db (Commit-State, ObjectChanges, Approvals, Ledger).
  • Forgejo-Zugriff: GitReader liest Repo nexo312/trading-system-docs (Forgejo-Git-Push-Key).
  • Tolaria-Zugriff: write() (SAVE) via TolariaClient.
  • Approval-DB-Zugriff: C5AStore (delete_approvals-Tabelle) — SAVE-Pfad liest/schreibt sie nicht aktiv.
  • Benötigte Inputs: Commit-SHA, ObjectChanges, Source-/Provenance-Daten aus Forgejo.
  • Erzeugte Outputs: Tolaria-SAVE, C5-State-Update (PROPAGATING→VERIFYING→UPDATING_SEARCH).

1b. DELETE-Pfad: RQ/Hermes → C5 CLI → DeleteExecutor → Approval Validation → TolariaClient.delete()

RQ/Hermes (Container 06a4a73573d0)
  │
  ├─ [Entry Point] rq_c5_cli.py cmd_c5_delete_approve (Z.355)  → persistiert Human-Approval
  ├─ [Entry Point] rq_c5_cli.py cmd_c5_delete_execute (Z.424)  → führt DELETE aus
  ├─ [Entry Point] rq_c5_cli.py cmd_c5_delete_replay (Z.458)   → idempotenter Replay
  │
  ├─ [Helper] rq_c5_cli.py _delete_executor (Z.344)
  │     TolariaClient(base_url, delete_token=os.environ.get(ENV_TOLARIA_DELETE_TOKEN))
  │     → liest NUR DELETE-Token (kein SAVE-Token)
  │
  ├─ [Library] rq_c5_delete.py DeleteExecutor (Z.83)
  │     __init__(store, client=None) → client = client or TolariaClient()  (Default credential-frei)
  │     execute(commit_sha) (Z.192):
  │       Gate 1: ObjectChange existiert, operation == DELETE
  │       Gate 2: Commit-Status == ST_DELETE_APPROVED
  │       Gate 3: Approval existiert, APPROVED, passt exakt (Commit/Objekt/Pfad)
  │       Gate 4: Pre-Delete-Drift-Check (Ziel existiert noch)
  │       → _do_delete() → self.client.delete() (DELETE)
  │
  └─ [Library] rq_c5c.py TolariaClient.delete() (Z.~352)
        token = self._require_token(self.delete_token, "DELETE")  # fail-closed
        → POST /api/vault/delete  (Bearer delete_token)

Boundary-Fakten (DELETE):

  • Process Boundary: DeleteExecutor läuft im RQ/Hermes-Prozess (kein separater Prozess).
  • Container Boundary: RQ-Container 06a4a73573d0SELBER Container wie SAVE.
  • Filesystem Boundary: /opt/data; C5-DB /opt/data/c5c_live/c5a.db.
  • ENV Boundary: TOLARIA_DELETE_TOKEN ABSENT im RQ-Container (verifiziert).
  • Network Boundary: Tolaria 187.124.31.123:5173.
  • DB-Zugriff: C5AStore → SQLite c5a.db (Approvals, Ledger, Commit-State).
  • Forgejo-Zugriff: GitReader (Provenance-/Object-Daten).
  • Tolaria-Zugriff: delete() (DELETE) via TolariaClient.
  • Approval-DB-Zugriff: C5AStore delete_approvals-Tabelle (lesen + USED markieren).
  • Benötigte Inputs: Commit-SHA, persistierte Approval, ObjectChange, Nonce.
  • Erzeugte Outputs: Tolaria-DELETE, Approval→USED, Commit→ST_UPDATING_SEARCH.

1c. Kernbefund (bestätigt AUTH.3B/3C)

SAVE- und DELETE-Caller laufen im SELBEN Prozess/Container (rq_c5_cli.py, RQ-Container 06a4a73573d0). CREDENTIAL_SEPARATION_STATUS = LOGICAL_ONLY. Sobald Credentials produktiv in denselben Container injiziert würden, wäre RQ_SAVE_ACCESS/DELETE_ACCESS technisch NICHT mehr NO. → AUTH.4 BLOCKED.

1d. DB-Schema (C5AStore, c5a.db) — rekonstruiert

  • Tabellen: commits (commit_sha, parent_sha, discovered_at, sequence, status, retry_count, last_error, last_error_code, updated_at), objects (commit_sha, object_id, path_before, path_after, operation, content_hash_before/after, metadata_hash_before/after, representation, state, reason_code; UNIQUE(commit_sha, object_id, operation)), delete_approvals (approval_id, workflow_commit, object_change_id, object_id, path, operation='DELETE', approval_status APPROVED/USED/REVOKED, approved_by, approval_nonce, approved_at, used_at; UNIQUE(workflow_commit, object_change_id, object_id, path)), kv (key, value — für search_source_provenance).
  • State Machine (commits.status): DISCOVERED → VALIDATING → READY → PROPAGATING_TOLARIA → VERIFYING_TOLARIA → UPDATING_SEARCH → VERIFYING_SEARCH → APPLIED; plus RETRY_PENDING, FAILED, DEAD, HUMAN_REVIEW_REQUIRED, WAITING_FOR_PREDECESSOR, DELETE_APPROVED, DELETING.
  • Locks/Transactions: SQLite; _init_schema erstellt Tabellen; upsert_commit nutzt INSERT ... ON CONFLICT DO UPDATE (idempotent); add_object_change nutzt INSERT OR IGNORE (idempotent per UNIQUE). Kein expliziter WAL-Modus im Code gesehen (Default SQLite).
  • Idempotenz: SyncStateMachine.idempotency_check (commit_sha+object_id+operation; ALREADY_APPLIED/ALREADY_AT_TARGET/RETRY_SAFE/CONFLICT); add_object_change INSERT OR IGNORE; upsert_commit ON CONFLICT.
  • Retry: retry_commit (increment_retry, max_retries → DEAD, sonst RETRY_PENDING + backoff); _is_retryable in C5C (nur TolariaUnavailableError retryable).
  • Replay: c5-delete-replay (idempotent, nutzt denselben _delete_executor); Approval single-use (→ USED).
  • Recovery: c5e-recover, c5e-reconcile (C5E); c5e-replay --allow-writes nur Test-/Canary-Scope.
  • Provenance: persist_search_source_provenance (kv-Tabelle, key search_source_provenance:<workflow_commit_sha>); get_source_provenance.
  • Mission Binding: FEHLT in C5A-Commits/ObjectChanges (kein mission_id-Feld). Nur in AUTH.3D-Approval-Payload (mission_id) vorhanden.
  • Object Binding: objects.object_id (UNIQUE mit commit_sha+operation); Approval object_id-bound.
  • Commit Binding: commits.commit_sha; Approval workflow_commit-bound.

1e. AUTH.3D-Approval (rekonstruiert, approval_payload.py/approval_state.py/approval_verifier.py)

  • Payload (12 kanonische Felder): approval_version, approval_id, mission_id, delete_request_id, operation, object_id, vault_path, expected_commit, expected_provenance_hash, nonce, issued_at, expires_at.
  • Lifecycle: CREATED → VERIFIED → RESERVED → EXECUTED → CONSUMED (atomare Reservation, TOCTOU-sicher).
  • Verifier: verify-only (Ed25519 PUBLIC KEY ONLY), importiert nur key_id, verify.
  • Signature: approval_signature.py enthält sign()/private_key_to_pem() NUR zur Simulation; KEIN Private Key im Stack.
  • Bindings: Object, Commit, Path, Operation, Single-Use, Nonce, Expiry, Mission (in Payload).

2. COMMAND CHANNEL OPTIONS (Phase 3) — Bewertung

Keine neue Infrastruktur bevorzugen, wenn eine einfachere bestehende Primitive die Sicherheitsanforderungen erfüllt.

Kriterium A) Shared SQLite Job DB B) Getrennte SQLite Inbox pro Executor C) Filesystem Inbox / atomic rename D) Interner HTTP Executor Endpoint E) RabbitMQ / Broker F) Unix Socket / IPC G) C5 State Machine (bestehend)
SECURITY Mittel Hoch Mittel Mittel Hoch Mittel Hoch
COMPLEXITY Niedrig Niedrig Niedrig Mittel Hoch Mittel Niedrig
FAIL_CLOSED Ja Ja Ja Ja Ja Ja Ja
AUDITABILITY Hoch Hoch Mittel Hoch Hoch Mittel Hoch
IDEMPOTENCY Ja Ja Teilweise Ja Ja Ja Ja
REPLAY_RESISTANCE Teilweise Ja Teilweise Ja Ja Ja Ja
CRASH_RECOVERY Ja Ja Ja Ja Ja Ja Ja
CONCURRENCY Mittel (Lock) Hoch (getrennte DBs) Mittel Hoch Hoch Mittel Hoch
RQ_SCOPE_ESCALATION Nein Nein Nein Nein Nein Nein Nein
SECRET_EXPOSURE Kein Secret Kein Secret Kein Secret Kein Secret Kein Secret Kein Secret Kein Secret
FILESYSTEM_BOUNDARY Geteilt Getrennt Getrennt Getrennt Getrennt Getrennt Getrennt
NETWORK_BOUNDARY Kein Netz Kein Netz Kein Netz Netz nötig Netz nötig Kein Netz Kein Netz
AUTH3D_COMPATIBILITY Ja Ja Ja Ja Ja Ja Ja
CURRENT_C5_COMPATIBILITY Mittel Hoch Mittel Niedrig Niedrig Niedrig Hoch
AUTH4_DEPLOYMENT_COMPLEXITY Niedrig Niedrig Niedrig Mittel Hoch Mittel Niedrig

Bewertung:

  • OPTION B (getrennte SQLite Inbox pro Executor) ist die bevorzugte Wahl: Sie erfüllt die geforderte GETRENNTE INBOX/STATE BOUNDARY (SAVE-Executor claimt nur SAVE-Jobs, DELETE-Executor nur DELETE-Jobs), nutzt vorhandene SQLite-Primitive (keine neue Infrastruktur), ist fail-closed, auditierbar, idempotent, replay-resistent und crash-recoverable. Getrennte DB-Dateien (c5a_save.db/c5a_delete.db) vermeiden Lock-Konflikte zwischen SAVE- und DELETE-Executor.
  • OPTION A (Shared SQLite Job DB) ist einfacher, aber teilt die DB → schwächere DB-Boundary (SAVE-Executor könnte DELETE-Jobs sehen, wenn Scope nicht strikt getrennt).
  • OPTION C (Filesystem Inbox) ist einfach, aber weniger robust (kein atomarer Claim/Lease, schwächere Idempotenz/Replay-Resistenz).
  • OPTION D (HTTP Endpoint) erfordert neue Netzwerk-Endpoints → Scope-Creep.
  • OPTION E (RabbitMQ/Broker) erfordert neue Infrastruktur → abgelehnt (nicht vorhanden).
  • OPTION F (Unix Socket) erfordert neue IPC-Infrastruktur → Scope-Creep.
  • OPTION G (C5 State Machine) ist die bestehende Primitive für DELETE (Approval persistiert, Nonce, Single-Use, Replay-Schutz) und wird als Komplement zu B genutzt: Der DELETE-Job verweist auf die persistierte AUTH.3D-Approval; der DELETE-Executor validiert Job UND Approval unabhängig.

Empfehlung: OPTION B (getrennte SQLite Inbox pro Executor) als Command Channel, kombiniert mit OPTION G (C5 State Machine) für die DELETE-Approval-Autorität. Keine neue Infrastruktur.


3. PREFERRED ARCHITECTURAL PROPERTY (Phase 4)

GETRENNTE INBOX / STATE BOUNDARIES.

RQ/Hermes (Container 06a4a73573d0) — credential-frei
  │  credential-freier Job-Request (kein Secret)
  ▼
+---------------------------+      +-----------------------------+
| c5-save-executor (Cont.)  |      | c5-delete-executor (Cont.)  |
| SAVE credential           |      | DELETE credential           |
| NO DELETE                 |      | NO SAVE                     |
| claimt NUR SAVE-Jobs      |      | claimt NUR DELETE-Jobs      |
| c5a_save.db (Inbox)       |      | c5a_delete.db (Inbox)       |
+---------------------------+      +-----------------------------+
  │  SAVE (Bearer save_token)        │  DELETE (Bearer delete_token)
  ▼                                  ▼
Tolaria Server (187.124.31.123:5173) — erwartet SAVE+DELETE-Verifier

Kein generischer Dispatcher. Kein command="...", endpoint="...", url="...", method="...", shell="...", python="...", handler="...". Keine RCE-artige Semantik.

Job Types als geschlossene Allowlist:

  • C5_SAVE_OBJECT
  • C5_DELETE_OBJECT

Gegen die reale C5-Architektur geprüft: Die C5-Operationen sind OP_CREATE, OP_CONTENT_UPDATE, OP_METADATA_UPDATE, OP_STATE_UPDATE, OP_TAGS_UPDATE, OP_SOURCE_CANONICAL_RELATION_UPDATE, OP_RENAME, OP_MOVE, OP_DELETE_REQUEST, OP_SUPERSEDE. Für den Command Channel werden diese auf zwei geschlossene Job-Types abgebildet: SAVE (alle Nicht-DELETE-Operationen, die Tolaria-Write erfordern) und DELETE (nur OP_DELETE_REQUEST). Kein generischer Operation-Dispatcher.


4. SAVE JOB CONTRACT (Phase 5)

Kanonischer SAVE Job

{
  "job_version": 1,
  "job_id": "uuid4",
  "mission_id": "uuid4",
  "job_type": "C5_SAVE_OBJECT",
  "object_id": "string",
  "vault_path": "/app/vault/...",
  "source_commit": "sha256",
  "provenance_hash": "sha256",
  "expected_state": "string",
  "created_at": "ISO8601 UTC",
  "idempotency_key": "uuid4"
}

Content-Entscheidung: A) Content im Job ODER B) Executor lädt selbst

Gewählt: B) Executor rekonstruiert sicherheitsrelevante Daten selbst aus autoritativer Source (Forgejo).

Begründung (Manipulationsrisiko):

  • Wenn Content im Job transportiert würde, könnte RQ (oder ein kompromittierter RQ-Prozess) beliebigen Content in den Job schreiben → der SAVE-Executor würde manipulierten Content nach Tolaria schreiben.
  • Bevorzugte Eigenschaft (aus Mission): Executor rekonstruiert sicherheitsrelevante Daten soweit möglich selbst aus autoritativen Quellen, statt RQ blind zu vertrauen.
  • Der SAVE-Executor lädt den Content selbst aus Forgejo (source_commit + object_id → GitReader.file_content) und validiert gegen provenance_hash/expected_state.

RQ darf NICHT bestimmen:

  • Tolaria Base URL (Executor-Konfiguration, nicht Job)
  • HTTP Method (fix: POST /api/vault/save)
  • Authorization Header (Executor-Credential, nicht Job)
  • Credential (Executor-Credential, nicht Job)
  • beliebigen Zielendpoint (fix: /api/vault/save)

5. DELETE JOB CONTRACT (Phase 6)

Kanonischer DELETE Job (strenger)

{
  "job_version": 1,
  "job_id": "uuid4",
  "mission_id": "uuid4",
  "delete_request_id": "uuid4",
  "job_type": "C5_DELETE_OBJECT",
  "object_id": "string",
  "vault_path": "/app/vault/...",
  "expected_commit": "sha256",
  "expected_provenance_hash": "sha256",
  "approval_id": "uuid4",
  "created_at": "ISO8601 UTC",
  "idempotency_key": "uuid4"
}

WICHTIG: Der DELETE Job selbst ist KEINE Approval.

DeleteExecutor muss unabhängig laden/verifizieren (AUTH.3D Approval Artifact):

  • Ed25519 Signature (verify-only, PUBLIC KEY ONLY)
  • mission_id
  • delete_request_id
  • object_id
  • vault_path
  • expected_commit
  • expected_provenance_hash
  • nonce
  • expiry
  • single-use state

Job und Approval müssen exakt zusammenpassen. Mismatch → DELETE_DENIED.


6. TRUST MODEL (Phase 7)

RQ_JOB_FIELDS_TRUST_LEVEL

Feld SAVE DELETE Klassifikation
job_version TRUSTED (Schema) TRUSTED (Schema) Schema-Validierung
job_id TRUSTED (Identität) TRUSTED (Identität) Idempotenz-Schlüssel
mission_id VERIFY_AGAINST_SOT VERIFY_AGAINST_SOT Gegen Approval/Provenance
job_type TRUSTED (Allowlist) TRUSTED (Allowlist) Geschlossene Allowlist
object_id VERIFY_AGAINST_SOT VERIFY_AGAINST_SOT Gegen Forgejo/Approval
vault_path VERIFY_AGAINST_SOT VERIFY_AGAINST_SOT Gegen ObjectChange/Approval + Path Safety
source_commit VERIFY_AGAINST_SOT Gegen Forgejo
provenance_hash RECOMPUTE VERIFY_AGAINST_SOT Executor rechnet selbst
expected_state VERIFY_AGAINST_SOT Gegen Forgejo
expected_commit VERIFY_AGAINST_SOT Gegen Approval
expected_provenance_hash VERIFY_AGAINST_SOT Gegen Approval
approval_id VERIFY_AGAINST_SOT Gegen AUTH.3D Approval
delete_request_id VERIFY_AGAINST_SOT Gegen Approval
created_at TRUSTED (Metadaten) TRUSTED (Metadaten) Audit
idempotency_key TRUSTED (Idempotenz) TRUSTED (Idempotenz) Replay-Schutz

Grundsatz: DATA FROM RQ != AUTHORITY. RQ ist Orchestrator, nicht Security Authority. Für sicherheitskritische Felder: VERIFY_AGAINST_SOT oder RECOMPUTE.


7. JOB STATE MACHINE (Phase 8)

Deterministische State Machine

CREATED → READY → CLAIMED → EXECUTING → SUCCEEDED
                              │
                              ├→ FAILED
                              ├→ REJECTED
                              └→ OUTCOME_UNKNOWN

Erlaubte Transitionen (geschlossene Allowlist):

  • CREATED → READY (Schema validiert, Job akzeptiert)
  • CREATED → REJECTED (Schema/Allowlist/Injection-Fehler)
  • READY → CLAIMED (atomarer Claim)
  • CLAIMED → EXECUTING (Claim bestätigt)
  • EXECUTING → SUCCEEDED (bestätigter Erfolg)
  • EXECUTING → FAILED (bestätigter Fehler, kein Mutationseffekt)
  • EXECUTING → OUTCOME_UNKNOWN (unklares HTTP-Ergebnis)
  • EXECUTING → REJECTED (Gate-Fehler während Execution)

RETRYABLE: Optional, nur sicher begründet. Für SAVE: RETRYABLE nur bei TolariaUnavailableError (transient, kein Mutationseffekt). Für DELETE: KEIN RETRYABLE — ein bereits gestarteter DELETE darf nicht wegen Lease-Expiry von einem zweiten Worker wiederholt werden. OUTCOME_UNKNOWN → kein blinder Retry.

Ungültige Transition → FAIL CLOSED (InvalidTransitionError).


8. CLAIM / LEASE / CONCURRENCY (Phase 9)

Claim-Modell

  • worker_id: UUID des Executors (SAVE-Executor vs DELETE-Executor).
  • claim_id: UUID pro Claim.
  • claimed_at: Timestamp.
  • lease_until: Timestamp (Claim-Expiry).
  • attempt_count: Zähler.

Atomarer Claim

Der Claim muss atomar sein (SQLite UPDATE ... WHERE status='READY' AND (lease_until IS NULL OR lease_until < now) RETURNING ... in einer Transaktion). Kein TOCTOU.

Concurrency-Prüfung

  • Zwei SAVE Worker: Nur einer kann denselben Job atomar claimen (Status-Transition READY→CLAIMED ist atomar).
  • Zwei DELETE Worker: Nur einer kann denselben DELETE-Job claimen. Ein bereits gestarteter DELETE darf NICHT einfach wegen Lease-Expiry von einem zweiten Worker wiederholt werden. → DELETE-Job, der in EXECUTING ist, wird bei Lease-Expiry in OUTCOME_UNKNOWN überführt (nicht zurück zu READY), und ein zweiter Worker darf ihn NICHT erneut claimen. Nur ein expliziter Human-/Admin-Eingriff (AUTH.3D-Approval erneut verifizieren) kann einen DELETE-Job aus OUTCOME_UNKNOWN reaktivieren.
  • Worker Crash nach Claim: Lease-Expiry → Job wird für SAVE wieder claimbar (RETRYABLE), für DELETE → OUTCOME_UNKNOWN (nicht blind wiederholen).
  • Stale Claim: Lease-Expiry erkennt stale Claims.
  • Restart / Doppelstart / Race: Atomarer Claim + Lease verhindern Doppelverarbeitung.

9. IDEMPOTENCY (Phase 10)

SAVE

Idempotenz über: job_id + idempotency_key + object_id + source_commit/provenance.

  • Gleicher Job (job_id) → bereits verarbeitet → kein zweiter Write.
  • Gleicher idempotency_key → idempotent (kein zweiter Write).
  • Gleicher object_id + source_commit → ALREADY_AT_TARGET (Ziel-Hash erreicht).

DELETE (strenger)

  • Nach bestätigtem DELETE: gleicher Job niemals erneut mutieren.
  • Bei unklarem HTTP-Ergebnis: OUTCOME_UNKNOWN. KEIN BLINDER RETRY.
  • AUTH.3D OUTCOME_UNKNOWN-Modell bleibt erhalten.
  • Approval single-use (→ USED/CONSUMED) → Replay denied.

10. JOB AUTHENTICITY / INTEGRITY (Phase 11)

Ist Job-DB-Manipulation durch RQ relevant?

  • Für SAVE: Ein manipulierter Job darf niemals Scope erweitern. Da der SAVE-Executor Content selbst aus Forgejo lädt und gegen provenance_hash/expected_state validiert, kann ein manipulierter Job keinen beliebigen Content einschleusen. Scope bleibt geschlossen (nur SAVE, nur erlaubter Pfad).
  • Für DELETE: Selbst vollständige Job-DB-Schreibkontrolle darf NICHT ausreichen, um DELETE zu autorisieren. AUTH.3D Signature bleibt die Autorität. Ein manipulierter DELETE-Job ohne gültige, exakt passende AUTH.3D-Approval → DELETE_DENIED.

Zusätzliche Schutzmechanismen (bewertet)

  • Job HMAC: NICHT nötig für DELETE (AUTH.3D-Signatur schützt die kritische DELETE-Autorität). Für SAVE optional, aber nicht erforderlich (Content wird rekonstruiert).
  • DB ownership / filesystem permissions: Ja — getrennte DB-Dateien mit Executor-spezifischen Permissions (siehe Phase 12).
  • Immutable fields after claim: Ja — nach CLAIMED sind job_type, object_id, vault_path, approval_id, expected_* unveränderlich (UPDATE verboten).
  • State hash / row version / compare-and-swap: Optional für SAVE; für DELETE wird die AUTH.3D-Signatur als Autorität genutzt (keine unnötige Kryptografie).

Keine unnötige Kryptografie einführen, wenn die AUTH.3D-Signatur bereits die kritische DELETE-Autorität schützt.


11. DB / FILESYSTEM BOUNDARY (Phase 12)

Getrennte SQLite-Dateien (bestätigt AUTH.3C)

  • c5a_save.db — SAVE-Inbox (SAVE-Jobs, SAVE-State)
  • c5a_delete.db — DELETE-Inbox (DELETE-Jobs, DELETE-State, Approval-Referenzen)

Zugriffsmatrix

DB RQ_READ RQ_WRITE SAVE_EXEC_READ SAVE_EXEC_WRITE DELETE_EXEC_READ DELETE_EXEC_WRITE
c5a_save.db Ja (Job erzeugen) Ja (Job erzeugen) Ja Ja NEIN NEIN
c5a_delete.db Ja (Job erzeugen) Ja (Job erzeugen) NEIN NEIN Ja Ja

Ziel:

  • SAVE Executor braucht keinen Zugriff auf DELETE Credential/DB, außer explizit notwendige read-only Referenzen (keine).
  • DELETE Executor braucht keinen SAVE-State.

Linux UID/GID-/Volume-Modell (für AUTH.4, NICHT produktiv ändern)

  • SAVE-Executor-Container: eigener User, Volume nur für c5a_save.db.
  • DELETE-Executor-Container: eigener User, Volume nur für c5a_delete.db.
  • RQ-Container: Volume für beide Inbox-DBs (nur Job-Erzeugung, kein Executor-Zugriff).
  • Kein geteiltes Secret-Volume.

12. NETWORK BOUNDARY (Phase 13)

SAVE Executor

ALLOW:

  • Tolaria notwendige interne Adresse (187.124.31.123:5173) — SAVE + READ
  • notwendige autoritative Source (Forgejo 10.0.4.1:3000) — Source-/Provenance-Read

DENY (soweit möglich):

  • Docker Socket
  • SSH
  • Host Root
  • Telegram
  • beliebiges Internet
  • DELETE-spezifische Dienste

DELETE Executor

ALLOW:

  • Tolaria (187.124.31.123:5173) — DELETE
  • notwendige Approval-/State-Source (Forgejo 10.0.4.1:3000) — Object/Commit-Read

DENY:

  • Docker Socket
  • SSH
  • Host Root
  • Telegram
  • beliebiges Internet
  • SAVE-spezifische Credentials

Forgejo-Runtime-Frage

Wird Forgejo wirklich zur Runtime benötigt?

  • SAVE: JA — der SAVE-Executor lädt Content selbst aus Forgejo (source_commit + object_id → GitReader.file_content). Forgejo ist die autoritative Source.
  • DELETE: JA (für Object/Commit-Read zur Validierung). Aber: Falls der DELETE-Job alle nötigen Daten (object_id, vault_path, expected_commit, expected_provenance_hash) bereits enthält und die AUTH.3D-Approval diese exakt bestätigt, könnte Forgejo-Read optional sein. Empfehlung: Forgejo-Read für DELETE beibehalten (Defense in depth), aber als read-only, nicht als Write.

Falls nicht nötig: KEIN Forgejo Runtime Access. Für SAVE ist er nötig (Content- Rekonstruktion). Für DELETE ist er nötig (Object/Commit-Validierung).


13. SECRET BOUNDARY (Phase 14)

Zielmatrix (beweisen)

Akteur SAVE_TOKEN DELETE_TOKEN PRIVATE_SIGNING_KEY PUBLIC_VERIFY_KEY
RQ ABSENT ABSENT ABSENT ABSENT
SAVE Executor PRESENT ABSENT ABSENT ABSENT
DELETE Executor ABSENT PRESENT ABSENT PRESENT
Rain ABSENT ABSENT ABSENT ABSENT
Tolaria Server PRESENT (verifier) PRESENT (verifier) ABSENT ABSENT

Beweis (Design):

  • RQ: credential-frei (kein SAVE/DELETE-Token, kein Private Key). Nur Job-Erzeugung.
  • SAVE Executor: erhält NUR SAVE-Token (injiziert), kein DELETE-Token, kein Private Key.
  • DELETE Executor: erhält NUR DELETE-Token + PUBLIC_VERIFY_KEY, kein SAVE-Token, kein Private Key.
  • Rain: kein Zugriff auf irgendein Secret (separate Container).
  • Tolaria Server: hält SAVE+DELETE-Verifier-Secrets (eigene ENV), KEIN Christians Private Key.

14. COMMAND INJECTION DEFENSE (Phase 15)

Jobs dürfen niemals enthalten oder ausführen:

  • shell commands
  • arbitrary executable
  • Python snippets
  • URLs
  • HTTP headers
  • credentials
  • SQL
  • filesystem absolute targets (außerhalb erlaubten Schemas)
  • Docker commands
  • SSH commands

Enum + typed schema:

  • job_type ist ein Enum (C5_SAVE_OBJECT | C5_DELETE_OBJECT), kein freier String.
  • Alle Felder sind typisiert (uuid, sha256-hex, ISO8601, enum).
  • Unknown job_type → REJECTED.
  • Extra privileged fields → REJECTED (Schema-Validierung lehnt unbekannte Felder ab).
  • Malformed schema → REJECTED.

15. PATH SAFETY COMPOSITION (Phase 16)

AUTH.2 Path Safety bleibt serverseitig zwingend. Executor muss zusätzlich eigene Pfadprüfung durchführen.

Defense in depth:

RQ Job → Executor validation → Tolaria AUTH.2 validation
  • Executor validation: vault_path wird gegen das erlaubte Schema geprüft (kein .., kein absoluter Pfad außerhalb /app/vault, keine Symlink-Escapes, keine Unicode-Ambiguität, keine encoded traversal).
  • Tolaria AUTH.2 validation: serverseitige Path Safety (bestehend, zwingend).
  • Kein Caller darf AUTH.2 Path Safety ersetzen.

16. AUTH.3D COMPOSITION (Phase 17)

Für DELETE muss gelten (alle Faktoren, keiner ersetzt einen anderen):

VALID_JOB
AND VALID_STATE
AND VALID_AUTH3D_SIGNATURE
AND VALID_NONCE
AND VALID_EXPIRY
AND NOT_CONSUMED
AND VALID_OBJECT
AND VALID_PATH
AND VALID_COMMIT
AND VALID_PROVENANCE
AND DELETE_CREDENTIAL

erst dann: Tolaria DELETE.


17. FAILURE MODEL (Phase 18)

Szenario Verhalten
DB unavailable FAIL CLOSED — kein Write ohne State
DB locked SQLite-Lock → Retry/Fehler; kein Write ohne State
malformed job REJECTED (Schema)
unknown version REJECTED
unknown type REJECTED
state mismatch FAIL CLOSED (InvalidTransitionError)
source unavailable FAIL CLOSED (kein Content-Rekonstruktions-Write)
Tolaria unavailable SAVE: RETRYABLE (transient); DELETE: OUTCOME_UNKNOWN
401 FAIL CLOSED (Credential/Scope-Fehler)
403 FAIL CLOSED
404 FAIL CLOSED (Ziel fehlt)
409 FAIL CLOSED (Konflikt)
500 FAIL CLOSED
timeout SAVE: RETRYABLE; DELETE: OUTCOME_UNKNOWN
connection reset SAVE: RETRYABLE; DELETE: OUTCOME_UNKNOWN
worker crash Lease-Expiry; SAVE: wieder claimbar; DELETE: OUTCOME_UNKNOWN
process kill dito
container restart Zustand aus DB; keine Sicherheitsinvariante verloren
duplicate job Idempotenz (job_id/idempotency_key)
duplicate worker Atomarer Claim verhindert Doppelverarbeitung
approval unavailable DELETE_DENIED
approval invalid DELETE_DENIED
approval expired DELETE_DENIED
credential missing FAIL CLOSED (kein HTTP)

Security-Grundsatz: Unsicherheit darf nicht zu privilegierter Mutation führen.


18. AUDIT MODEL (Phase 19)

Jeder Job benötigt Audit Trail. Mindestens:

  • job_id, mission_id, job_type, object_id
  • state transitions (mit Timestamps)
  • worker_id, attempt
  • timestamps
  • result code
  • approval_id (bei DELETE)
  • provenance

NICHT loggen:

  • Credentials
  • Authorization Header
  • Private Key
  • sensitive Approval internals, falls unnötig

Audit muss unterscheiden:

  • REQUESTED (RQ hat Job erzeugt)
  • AUTHORIZED (Executor hat Job + Approval validiert)
  • EXECUTED (Mutation ausgeführt)

WICHTIG: RQ REQUEST != HUMAN AUTHORIZATION. Ein REQUESTED-Eintrag ist keine AUTHORIZATION. Nur AUTHORIZED (nach AUTH.3D-Validierung) ist eine Autorisierung.


19. ISOLATED IMPLEMENTATION (Phase 20) — Design

Implementiert in Code + Tests. KEIN produktiver Container. Modulare Komponenten.

Module

  • job_schema.py — Job-Schema-Validierung (Enum, typed, Allowlist, REJECTED)
  • job_store.py — SQLite Inbox (getrennte DBs, atomarer Claim, Lease)
  • job_state_machine.py — deterministische State Machine (FAIL CLOSED)
  • job_claim.py — atomarer Claim/Lease/Concurrency
  • save_executor_core.py — SAVE-Executor-Logik (SAVE-only, Content-Rekonstruktion)
  • delete_executor_core.py — DELETE-Executor-Logik (DELETE-only, AUTH.3D-Validierung)

Wiederverwendung

  • Bestehende C5-Module (rq_c5a, rq_c5c, rq_c5_delete, approval_*) werden wiederverwendet, nicht neu geschrieben.
  • Keine Big-Bang-Neuschreibung.

20. TEST CONTRACT (Phase 21) — Design

T1T40 (siehe Mission §21). Implementiert in isolierten Tests (synthetische Secrets, Fake-Tolaria, Temp-DB). Keine produktiven Mutationsprobes.


21. ADVERSARIAL TESTS (Phase 22) — Design

JSON duplicate keys, Unicode path ambiguity, encoded traversal, job_type casing, enum confusion, integer/string confusion, oversized payload, stale mission, stale provenance, race claim, replay after restart, DB row manipulation, cross-executor DB injection, approval substitution, object substitution after claim, path substitution after claim, state mutation after approval, crash before HTTP, crash after uncertain HTTP.


22. TEST SENSITIVITY (Phase 23) — Design

Mutationen AL (siehe Mission §23). Jede Mutation muss relevante Tests ROT machen. Falls nicht: Tests verstärken.


23. REGRESSION (Phase 24) — Design

Volle relevante C5-Test-Suite + AUTH.3A-Tests + AUTH.3D-Tests. Keine bestehenden Sicherheitsinvarianten abschwächen. Baseline-Fails separat klassifizieren. Keine unrelated Reparaturen.


24. SECURITY REVIEW (Phase 25) — Design

Explizit beweisen: RQ_CREDENTIAL_FREE, HERMES_CREDENTIAL_FREE, RAIN_CREDENTIAL_FREE, SAVE_EXECUTOR_SAVE_ONLY, DELETE_EXECUTOR_DELETE_ONLY, DELETE_EXECUTOR_VERIFY_ONLY, NO_PRIVATE_SIGNING_KEY_IN_STACK, NO_GENERIC_COMMAND_DISPATCH, NO_ARBITRARY_URL, NO_ARBITRARY_HTTP_METHOD, NO_ARBITRARY_HEADERS, NO_SHELL_EXECUTION, JOB_DOES_NOT_EQUAL_AUTHORIZATION, AUTH3D_REQUIRED_FOR_DELETE, ATOMIC_CLAIM, IDEMPOTENCY, REPLAY_PROTECTION, OUTCOME_UNKNOWN_FAIL_CLOSED.


25. DESIGN DOC RECONCILIATION (Phase 26) — Design

AUTH3B_DESIGN.md, AUTH3C_DESIGN.md, AUTH3D_DESIGN.md (untracked). Entscheidungsmatrix (FILE, SECURITY_RELEVANCE, UNIQUE_INFORMATION, ALREADY_REPRESENTED_IN_COMMITTED_CODE/DOCS, STALE_INFORMATION, RECOMMENDED_SOT_ACTION; Klassifikation COMMIT/MERGE_INTO_ARCH_DOC/ ARCHIVE/DISCARD/KEEP_UNTRACKED_TEMPORARILY). Nur Empfehlung. Keine Mutation ohne separate Freigabe.


26. COMMIT / PUSH (Phase 27) — Design

Nur AUTH.3E-Code + Tests + ausdrücklich notwendige AUTH.3E-Dokumentation. Vor Commit: git status, git diff, Secret Scan, Private-Key Scan, Credential Pattern Scan. Keine echten Credentials. Nur synthetische Testwerte. FF-only Push. HEAD == origin/main verifizieren. Falls Hooks scheitern: Ursache analysieren. --no-verify NICHT automatisch verwenden.


27. FRESH CHECKER (Phase 28) — Design

Unabhängiger Checker (32 Punkte, siehe Mission §28).


28. FINAL REPORT (Phase 29) + HARD STOP (Phase 30)

Nach FINAL REPORT sofort STOP. Keine produktiven Mutationen, keine Container, keine echten Credentials, keine Hermes-Autonomie.