ProofedMesh/docs/architecture/v1.1.md

596 lines
32 KiB
Markdown
Raw 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.

# Technische Zielarchitektur: Vertrauenswürdige Kommunikation zwischen Menschen, KI-Agenten und beliebigen Prozess-Senken
**Status:** Architekturvorschlag V1.1 (aktive Diskussionsgrundlage)
**Vorgänger:** [`v1.0.md`](v1.0.md) — ursprünglicher Entwurf (unverändert archiviert)
**Letzte Änderung:** Senken-Konzept generalisiert — SAP ist nur eine mögliche Senke neben eigenen DB-Applikationen, HTTP-Webhooks, Message-Queues u.a.
Diese Lösung wird als **Identity-Aware Agent Messaging Platform** konzipiert: eine Plattform, die Benutzerfreigaben, delegierte Berechtigungen und sämtliche Verarbeitungsschritte kryptografisch nachvollziehbar macht.
Die zentrale Designentscheidung lautet:
> **Der Benutzer autorisiert den ursprünglichen Geschäftsinhalt. Jeder Agent attestiert anschließend seine eigene Verarbeitung. Die empfangende Senke überprüft die vollständige Kette, bevor sie eine Aktion ausführt.**
Dabei werden Identität, Berechtigung, Verarbeitung und Nachrichtentransport strikt getrennt.
## 0. Änderungen seit V1.0
Diese Revision setzt die Richtungsentscheidungen der ersten beiden Review-Runden um:
- **Scope Evidence Store:** kein rechtssicherer, kein WORM-/GoBD-Anspruch. Ziel ist der kryptografische Nachweis einer unverfälschten Kommunikation während eines **begrenzten Zeitraums** (Default: 1 Jahr).
- **PII-Trennung:** verbindlich. Attestierungen enthalten nur Hashes und Referenzen; personenbezogene Inhalte liegen in einem separaten, redaktionierbaren PII-Store.
- **IdP:** **Pocket ID** wird einziger IdP — für Menschen *und* Agenten (per `client_credentials`-Grant). Kein Keycloak.
- **Content Binding:** eigener Abschnitt 4.0 als verbindliches UX-/Signatur-Protokoll (WebAuthn über Content-Hash + visuelle Prüfziffer).
- **Workload-Identität:** doppelte Prüfung — Pocket-ID-Token *plus* K8s-ServiceAccount-Token.
- **Schlüsselbund-Scope:** Rotation Default 90 Tage × Retention 1 Jahr → max. ~5 aktive Schlüssel pro Agent.
- **Transparency Log:** Trillian + PostgreSQL (nicht Cassandra — Begründung in 8.3).
- **Replay-Schutz und fachliche Integritätsprüfung:** verbindliche Abschnitte (5.1 und 6.1).
- **Transport:** Matrix nur optional in Phase 5 für Mensch-Chat; Agent↔Agent läuft über HTTPS/mTLS + NATS JetStream.
- **Signing Service:** eigenes Deployment ab Tag 1.
- **predicateType-Namespace:** `https://basicstack.de/attestations/<domain>/<version>`.
- **🆕 Senken-Generalisierung:** SAP ist eine von vielen möglichen Prozess-Senken. Der Trust-Core ist senken-agnostisch. Senken-Adapter sind eine austauschbare Schicht mit festem Vertrag (Abschnitt 7).
## 1. Zielarchitektur
Logische Komponentenübersicht, keine direkte Netzwerktopologie. Freigabe- und Attestierungsnachweise werden über eigene APIs erstellt und geprüft; der Transport (HTTPS/mTLS + NATS, optional Matrix) überträgt nur Referenzen und Nachrichten — nie die Vertrauensentscheidung.
Der Prozess endet an einer **Senke** (Sink). Senken sind nach außen offene Zielsysteme — ERP (SAP S/4HANA, SAP Concur), eigene Datenbankanwendungen, HTTP-Webhooks externer Dienste, Message-Queues (Kafka/NATS), Dateiablage (SFTP, S3), Legacy-Systeme (Mail/EDI) oder beliebige kundeneigene APIs. Der Trust-Core ist senken-agnostisch; pro Senken-Typ gibt es einen **Senken-Adapter**, der denselben Vertrag erfüllt (Abschnitt 7).
## 2. Die fünf Architekturprinzipien
1. **Unveränderliche Benutzerfreigabe** — ein vertrauenswürdiger Dienst bestätigt den konkret autorisierten Geschäftsinhalt.
2. **Eigene Identität jedes Agenten** — jeder Agent besitzt eine separate kryptografische Identität und signiert seine eigenen Verarbeitungsschritte.
3. **Verkettete Attestierungen** — jeder Verarbeitungsschritt referenziert exakte Eingabe- und Ausgabe-Hashes.
4. **Unabhängige Autorisierung** — eine Signatur allein erteilt keine Berechtigung; eine Policy Engine kontrolliert erlaubte Transformationen und Senken-Aktionen.
5. **Transportneutralität** — Nachweise funktionieren über HTTP, NATS, Queue oder Matrix, ohne dass die kryptografische Vertrauenskette verloren geht.
Zusatzprinzip: **Senken-Neutralität.** Weder der Trust-Core noch der Verifikationspfad kennen die Semantik einer konkreten Senke. Senken-Adapter übersetzen zwischen dem senken-agnostischen „Decision"-Objekt und dem jeweiligen Zielsystem.
## 3. Konkrete technische Komponenten
| Dienst | Technologie | Verantwortung |
| --------------------- | ---------------------------------- | --------------------------------------------------- |
| Identity Provider | **Pocket ID** | Benutzeranmeldung (Passkey) und Agent-Clients (OIDC) |
| Approval Service | Go, REST API | Inhaltsgebundene Benutzerfreigabe |
| Agent Registry | PostgreSQL | Agentenidentitäten, Schlüsselhistorie, Berechtigungen |
| Signing Service | Go + OpenBao (Transit) | DSSE-Signaturen, verifiziert Agenten-Identität doppelt |
| Attestation Service | Go + in-toto | Verarbeitungsschritte dokumentieren |
| Policy Decision Point | Open Policy Agent (OPA) | Berechtigungen und Transformationsregeln |
| Verification Service | Go | Gesamte Vertrauenskette überprüfen |
| Transparency Log | **Trillian + PostgreSQL** | Append-only Merkle-Log mit Inclusion Proofs |
| Message Transport | HTTPS/mTLS + **NATS JetStream** | Agent↔Agent-Kommunikation |
| Chat Transport (opt.) | Matrix/Synapse | Mensch↔Agent-Chat (nur Phase 5) |
| **Sink Registry** | PostgreSQL | **Registrierte Senken, Adapter-Metadaten, Audience-Keys** |
| **Senken-Adapter** | **pro Senke; siehe Abschnitt 7** | **Übersetzt verifizierte Decision in Senken-Aktion** |
| Evidence Store | PostgreSQL (Metadaten) + S3 | DSSE-Envelopes und Attestate (Retention: 1 Jahr) |
| PII-Store | Verschlüsselt in S3 | Belege und Geschäftsdaten, redaktionierbar |
Approval, Attestation, Registry (Agent + Sink) und Verification bleiben in **einem modularen Go-Dienst** zusammengefasst. **Signing Service läuft als eigenes Deployment**. **Senken-Adapter** laufen pro Senken-Typ als eigenes Deployment oder — bei trivialen Senken — als Thin-Client im Buchungs-Agenten.
### 3a. Identity Provider: Pocket ID allein genügt
Pocket ID unterstützt neben dem Authorization-Code-Flow auch den OIDC **`client_credentials`-Grant** (Endpunkt `POST /api/oidc/token`) und bietet eine Standard-OIDC-Discovery inkl. JWKS unter `https://auth.basicstack.de/.well-known/openid-configuration`. Damit:
- **Menschen** melden sich per Passkey (WebAuthn) über den Authorization-Code-Flow an.
- **Agenten** werden in Pocket ID als eigene OIDC-Clients registriert (ein Client pro Agent). Jeder Client erhält `client_id` + `client_secret`, hinterlegt als K8s-Secret im Agent-Namespace. Agenten holen sich daraus kurzlebige Access-Tokens.
- **Senken** (insb. externe wie SAP) verifizieren per Pocket-ID-JWKS. Keine Federation nötig.
## 4. Datenmodell der Vertrauenskette
Jeder Vorgang erhält eine unveränderliche `workflow_id`. Jede Fassung der Daten wird als eigenes Artefakt mit SHA-256-Hash gespeichert. Der *Hash* liegt im Evidence Store; der *Inhalt* (PII) im separaten PII-Store.
### Vertrauens- und Verarbeitungskette (allgemein, am Beispiel Reisekosten → beliebige Senke)
1. **Benutzerfreigabe** — Approval Service bindet die Signatur an den Hash des Inhalts, den der Benutzer per Content Binding bestätigt hat (siehe 4.0), und an die OIDC-Identität des Mitarbeiters.
2. **Agent A: Vorbereitung** — Agent A signiert eine Attestierung über Eingabe-Hash, Ausgabe-Hash, Transformationsart und ursprüngliche Freigabe.
3. **Agent B: Mapping auf Senken-Datenmodell** — Agent B signiert seine eigene Attestierung mit Referenz auf die Ausgabe von Agent A.
4. **Agent C: Senken-Übergabe** — Agent C überprüft die komplette Kette, führt die Aktion über den passenden **Senken-Adapter** aus und erzeugt nach erfolgreicher Übergabe einen signierten Übergabenachweis mit Senken-Belegreferenz.
Attestierungen sind keine Chatnachrichten, sondern eigenständige, überprüfbare Datensätze.
### 4.0 Content Binding bei der Benutzerfreigabe
Risiko: der Browser könnte dem Benutzer etwas anderes zeigen als dem Approval Service.
Verbindlicher Ablauf:
1. Approval Service rendert den Freigabe-Inhalt serverseitig, berechnet SHA-256 `H_content` und speichert `(session_id, H_content, salt)` mit einmaligem `challenge_nonce`.
2. Browser zeigt den Inhalt *und* eine aus `H_content + challenge_nonce` abgeleitete, menschenlesbare **Prüfziffer** (z.B. 6 Zeichen Base32).
3. Benutzer bestätigt per Passkey (WebAuthn-Assertion über `challenge_nonce || H_content`).
4. Approval Service verifiziert die WebAuthn-Assertion, prüft Session-Integrität, erzeugt erst dann die DSSE-signierte Approval-Attestierung.
Damit scheitert ein kompromittierter Browser, der etwas anderes anzeigt, am visuellen Prüfziffer-Vergleich *oder* am WebAuthn-Signaturinhalt.
### 4.1 Benutzerfreigabe (DSSE-Statement)
Beachte: `audience` zeigt auf eine **registrierte Senke** aus der Sink Registry (3b), nicht hart auf „SAP".
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "expense/EXP-2026-0042/v1",
"digest": { "sha256": "<64 hex characters>" }
}
],
"predicateType": "https://basicstack.de/attestations/approval/v1",
"predicate": {
"workflow_id": "WF-0042",
"approval_id": "APR-0042",
"schema": { "name": "expense.canonical", "version": "1.0.0" },
"issuer": "approval-service",
"user": {
"iss": "https://auth.basicstack.de",
"sub": "user-12345"
},
"content_binding": {
"method": "webauthn-content-hash-v1",
"assertion_hash": "sha256:<webauthn assertion hash>",
"verification_code": "F3K-9MQ"
},
"action": "expense.submit",
"audience": "sink:sap-s4hana-prod",
"allowed_transformations": ["expense.normalize", "expense.map_to_sink"],
"allowed_sinks": ["sink:sap-s4hana-prod", "sink:expense-db-app"],
"nonce": "<base64 nonce for replay protection>",
"issued_at": "2026-10-25T10:30:00Z",
"expires_at": "2026-11-01T00:00:00Z"
}
}
```
Neu: `allowed_sinks` erlaubt in der Freigabe explizit mehrere Zielsenken (z.B. Dual-Write SAP + eigene DB).
### 4.2 Signatur mit DSSE
```json
{
"payloadType": "application/vnd.in-toto+json",
"payload": "<base64-encoded statement>",
"signatures": [
{ "keyid": "approval-service-key-2026-q4", "sig": "<base64-encoded signature>" }
]
}
```
DSSE's Pre-Authentication Encoding (PAE) signiert Nutzlast und Inhaltstyp gemeinsam. `keyid` ist nur Hinweis — die Identität ergibt sich aus der verifizierten Signatur gegen den zur `issued_at` gültigen Schlüssel in der Agent Registry.
### 4.3 Transformations-Attestierung (Beispiel Agent B — Mapping auf eine konkrete Senke)
```json
{
"_type": "https://in-toto.io/Statement/v1",
"subject": [
{
"name": "expense/EXP-2026-0042/sink-payload",
"digest": { "sha256": "<output hash>" }
}
],
"predicateType": "https://basicstack.de/attestations/transform/v1",
"predicate": {
"workflow_id": "WF-0042",
"actor": "agent-sink-mapping",
"operation": "expense.map_to_sink",
"sink_target": "sink:sap-s4hana-prod",
"materials": [
{ "name": "expense/EXP-2026-0042/normalized", "sha256": "<input hash>" }
],
"parent_attestation": "sha256:<previous proof hash>",
"approval_ref": "APR-0042",
"schema": {
"input": { "name": "expense.canonical", "version": "1.0.0" },
"output": { "name": "sink.sap.expense", "version": "1.2.0" }
},
"transform_version": "1.3.0",
"issued_at": "2026-10-25T10:31:15Z"
}
}
```
Agent B signiert diese Attestierung mit seinem eigenen Schlüssel. Wird dieselbe Freigabe auf eine zweite Senke abgebildet (z.B. „sink:expense-db-app"), erzeugt Agent B eine **zweite** Attestierung mit anderem `sink_target` und `output`-Schema.
### 4.4 Agentenregistrierung und Schlüsselhistorie
Jeder Agent ist in zwei Systemen eingetragen:
- **Pocket ID:** als OIDC-Client (`client_id`, `client_secret` im K8s-Secret).
- **Agent Registry (Postgres):** Identität plus Signatur-Schlüsselhistorie.
```sql
CREATE TABLE agents (
agent_id TEXT PRIMARY KEY,
pocket_id_client TEXT UNIQUE NOT NULL,
k8s_namespace TEXT NOT NULL,
k8s_service_account TEXT NOT NULL,
display_name TEXT NOT NULL,
allowed_actions TEXT[] NOT NULL,
created_at TIMESTAMPTZ NOT NULL
);
CREATE TABLE agent_keys (
key_id TEXT PRIMARY KEY,
agent_id TEXT NOT NULL REFERENCES agents(agent_id),
public_key_pem TEXT NOT NULL,
valid_from TIMESTAMPTZ NOT NULL,
valid_to TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
revocation_reason TEXT,
archived_at TIMESTAMPTZ
);
CREATE INDEX idx_agent_keys_lookup
ON agent_keys (agent_id, archived_at)
WHERE archived_at IS NULL;
```
**Validierung alter Attestierungen** nutzt den Schlüssel, der zur `issued_at` galt (TUF-Pattern).
**Retention und Performance.** Rotation Default 90 Tage, Nachweis-Retention 1 Jahr → max. ~5 nicht-archivierte Schlüssel/Agent. Nach Retention: `archived_at` wird gesetzt; Attestierungen jenseits der Retention werden explizit mit `expired_retention` abgelehnt.
### 4.5 Sink Registry
Senken sind **registrierte Entitäten** mit eigener Identität, Audit-Vertrag und — falls sinnvoll — eigenem Krypto-Schlüsselmaterial für Übergabenachweise:
```sql
CREATE TABLE sinks (
sink_id TEXT PRIMARY KEY, -- z.B. "sink:sap-s4hana-prod"
display_name TEXT NOT NULL,
adapter_kind TEXT NOT NULL, -- "sap_abap" | "db_app" | "http_webhook" | "message_queue" | "file_drop" | "custom"
endpoint_url TEXT, -- primary endpoint (sofern HTTP)
idempotency_scope TEXT NOT NULL, -- "payload_hash" | "workflow_id" | "sink_defined"
callback_pubkey_pem TEXT, -- optional: Senke signiert eigene Buchungsnachweise
allowed_actions TEXT[] NOT NULL,
owner_team TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL,
retired_at TIMESTAMPTZ
);
```
Der Verification Service verifiziert bei jedem Aufruf: `requested_action ∈ sink.allowed_actions` und `sink.retired_at IS NULL`.
## 5. Verifikation beim empfangenden Senken-Adapter
Der Senken-Adapter (oder — bei Thin-Client-Senken — Agent C) darf sich nicht auf die mitgesendeten IDs verlassen. Er muss die gesamte Nachweiskette prüfen.
```
POST /api/v1/attestations/verify
Authorization: Bearer <service-token>
Content-Type: application/json
```
```json
{
"workflow_id": "WF-0042",
"artifact_digest": "sha256:<final hash>",
"requested_action": "expense.post",
"target": "sink:sap-s4hana-prod"
}
```
Prüfungen:
| Prüfung | Fehlerfall |
| -------------------------------------------------------------------- | --------------------- |
| Signatur der Benutzerfreigabe gültig (Schlüssel zur `issued_at` gültig)? | Ablehnen |
| Content-Binding-Assertion in der Approval-Attestierung vorhanden? | Ablehnen |
| Benutzeridentität im Pocket-ID-JWKS auflösbar? | Ablehnen |
| Freigabe gültig, nicht widerrufen, nicht abgelaufen? | Ablehnen |
| `target` in Sink Registry registriert und `allowed_actions` erlaubt die Aktion? | Ablehnen |
| `target` in `approval.allowed_sinks` enthalten? | Ablehnen |
| Alle Agentensignaturen gültig gegen `agent_keys` zur `issued_at`? | Ablehnen |
| Agenten zum Signaturzeitpunkt registriert und berechtigt? | Ablehnen |
| Input-/Output-Hashes der Materialien-Kette konsistent? | Ablehnen |
| Transformationen laut Freigabe zulässig? | Ablehnen |
| Schema-Version der kanonischen Daten mit Approval kompatibel? | Ablehnen |
| Nutzlast entspricht freigegebener Geschäftssemantik (OPA)? | Ablehnen |
| `payload_hash` bereits für `target` übergeben? | Keine erneute Übergabe |
Erfolgsantwort:
```json
{
"valid": true,
"workflow_id": "WF-0042",
"original_user": { "issuer": "https://auth.basicstack.de", "subject": "user-12345" },
"actors": ["agent-travel", "agent-sink-mapping"],
"authorized_action": "expense.post",
"artifact_digest": "sha256:<final hash>",
"decision_id": "DEC-2026-0042",
"decision_nonce": "<base64>",
"bound_to_agent": "agent-sink-posting",
"bound_to_target": "sink:sap-s4hana-prod",
"expires_at": "2026-10-25T10:36:00Z"
}
```
### 5.1 Replay-Schutz (verbindlich)
- `decision_id` enthält einen `decision_nonce`, Lebensdauer 5 Minuten.
- Decision ist gebunden an `(agent_id, payload_hash, target)` — Zieländerung oder Agent-Wechsel invalidiert sie.
- Verification Service hält eine Postgres-Tabelle `decisions` mit `UNIQUE (payload_hash, target)` für den Doppelübergabe-Check.
- Jeder Senken-Adapter führt eine **eigene** Replay-Tabelle (`sink_decision_log`) mit `UNIQUE (decision_id)` und `UNIQUE (payload_hash)`. Defence in Depth, denn der Trust-Core kennt die Idempotenz-Eigenschaften der Senke nicht.
- Doppelübergabe wird gegen `payload_hash` (nicht nur `workflow_id`) geprüft.
## 6. Berechtigungssteuerung mit Open Policy Agent
Beispiel-Regeln (bewusst nicht ERP-spezifisch):
- Agent A darf Belege extrahieren und Geschäftsdaten strukturieren.
- Agent B darf Kontierungs- oder Zieldaten ergänzen und Felder auf das Senken-Schema zuordnen.
- Agent B darf keine Beträge oder Währungen ohne zusätzliche Freigabe ändern.
- Agent C darf ausschließlich vollständig validierte Vorgänge an Senken übergeben, und zwar nur an Senken aus `allowed_sinks` der Freigabe.
- Pro Senken-Typ können zusätzliche Policies greifen (z.B. „Senke `db-app` erlaubt Updates, Senke `sap-s4hana-prod` nur Inserts").
Rego-Policies werden zentral verwaltet und versioniert; die zum Entscheidungszeitpunkt verwendete Policy-Version wird im Evidence Store mit abgelegt.
### 6.1 Fachliche Integritätsprüfung — Schema-Versionierung (verbindlich)
Ein Hashvergleich allein reicht nicht, weil sich beim Mapping das technische Format ändert. Deshalb binden wir die Freigabe an ein **versioniertes kanonisches Schema**:
```json
{
"schema": { "name": "expense.canonical", "version": "1.0.0" },
"employee_id": "EMP-12345",
"currency": "EUR",
"total_amount": "428.50",
"expense_items": [
{ "type": "HOTEL", "amount": "328.50" },
{ "type": "MEALS", "amount": "100.00" }
]
}
```
Verbindlich:
- Kanonische Schemas und Senken-spezifische Zielschemas liegen versioniert in `schemas/` im Repo.
- Jede Approval-Attestierung signiert `schema.name` und `schema.version` mit.
- Jede Transform-Attestierung signiert Input- *und* Output-Schema plus `sink_target`.
- OPA-Policies arbeiten gegen das kanonische Schema; sie prüfen z.B. „Agent B darf `total_amount` nicht verändern" oder „Agent B darf `expense_items[].amount` höchstens kaufmännisch runden".
- Schema-Änderungen sind Breaking Changes und erfordern eine neue Hauptversion.
## 7. Senken-Integration (generalisiert)
Der Trust-Core ist senken-agnostisch. Jede Senke wird über einen **Senken-Adapter** angebunden, der denselben Vertrag erfüllt. SAP S/4HANA ist eine Senken-Implementierung; weitere Beispiele siehe 7.1–7.5.
### 7.0 Vertrag jedes Senken-Adapters
Jeder Adapter MUSS:
1. **Verifizieren** — ruft `POST /api/v1/attestations/verify` auf (oder bettet die Verifikationslogik mit eingezogenen Schlüsseln ein) und bricht bei `valid=false` oder abgelaufener Decision ab.
2. **Idempotent sein** — führt eine eigene Tabelle `sink_decision_log` mit `UNIQUE (decision_id)` und `UNIQUE (payload_hash)`.
3. **Audit-Mindestdaten speichern** — pro erfolgreicher Übergabe: `workflow_id`, `approval_id`, `decision_id`, `payload_hash`, `agent_id`, `original_user_sub`, `sink_record_ref` (z.B. Belegnummer / DB-ID / Message-ID / Dateipfad), `created_at`.
4. **Übergabenachweis zurückgeben** — signierte Antwort (ideal: Senke besitzt eigenen Schlüssel in `sinks.callback_pubkey_pem`; Fallback: Agent C signiert die unsignierte Senken-Antwort inkl. `sink_record_ref` selbst).
5. **Fehlerpfad definieren** — nach N fehlgeschlagenen Versuchen fließt der Vorgang in eine senken-eigene Operator-Queue oder meldet strukturierten Fehler an den Trust-Core (`sink_delivery_failed`-Attestierung).
6. **Mapping zu Business-IDs** darf nur serverseitig passieren — eine vom Agenten gelieferte Personalnummer / Benutzer-ID / Kostenstelle darf eine im Senken-System hinterlegte Zuordnung nicht überschreiben.
Die Verification Service-Antwort ist senken-agnostisch (`decision_id`, `bound_to_target`, `bound_to_agent`, `authorized_action`). Was die Senke damit tut, ist adapter-spezifisch.
### 7.1 Beispiel: SAP S/4HANA (Senke `sink:sap-s4hana-prod`)
ABAP-Integrationsschicht statt direktem Agentenzugriff auf Buchungsfunktionen.
Fluss: `Agent C` → `ABAP REST Endpoint` → `SAP-Anwendungslogik`.
```
POST /sap/bc/zexpense/v1/submit
Authorization: Bearer <sap-agent-token>
Idempotency-Key: WF-0042
Content-Type: application/json
```
```json
{
"workflow_id": "WF-0042",
"approval_id": "APR-0042",
"verification_ref": "DEC-2026-0042",
"expense_ref": "EXP-2026-0042",
"payload_hash": "sha256:<final hash>"
}
```
Vor der Buchung ruft ABAP den Validierungs-Webservice:
```
POST /api/v1/decisions/{decision_id}/replay-check
```
Rückgabe: ob Decision noch gültig, nicht verbraucht, an Agent C und Target gebunden.
**sap-agent-token** wird manuell im SAP-System hinterlegt. Rotation: neuer JWT (90 Tage) wird vom Signing Service erzeugt, Operator trägt in SM59 ein, alte und neue Token sind während Rollover parallel gültig.
**SAP-Audit** liegt in Tabelle `ZAGENT_AUDIT` (`WORKFLOW_ID`, `APPROVAL_ID`, `USER_SUB`, `PERNR`, `AGENT_ID`, `PAYLOAD_HASH`, `VERIFICATION_ID`, `DECISION_ID`, `BUKRS/BELNR/GJAHR`, `CREATED_AT`), mit `UNIQUE (WORKFLOW_ID)` und `UNIQUE (DECISION_ID)`.
### 7.2 Beispiel: Eigene Datenbankapplikation (Senke `sink:expense-db-app`)
Minimale Senken-Variante: eine selbst gebaute Postgres-App (eigener Microservice).
Fluss: `Agent C` → HTTPS/mTLS → `expense-db-adapter` (Go-Service) → `expense-postgres`.
```
POST /api/v1/expense/submit
Authorization: Bearer <agent-c-oidc-token> # Pocket-ID-Access-Token
Content-Type: application/json
```
Der Adapter:
- verifiziert das Pocket-ID-Token gegen JWKS,
- ruft Verification Service (`POST /api/v1/attestations/verify`),
- schreibt in `sink_decision_log` (Idempotenz),
- führt die Business-Insert/Update-Transaktion aus,
- gibt `sink_record_ref` (z.B. `db:expense_app/expenses/<uuid>`) an Agent C zurück.
Idempotenz: `UNIQUE (decision_id)` und `UNIQUE (payload_hash)` in `sink_decision_log`. Business-Tabelle hat `UNIQUE (workflow_id)`.
Vorteil gegenüber SAP: keine externe Krypto-Vertrauensbasis nötig; Adapter kann unmittelbar auf Agent Registry und JWKS zugreifen.
### 7.3 Beispiel: Generischer HTTP-Webhook (Senke `sink:custom-webhook-*`)
Für externe Dienste, die nur „einen HTTP-POST entgegennehmen" können.
```
POST <sink.endpoint_url>
Authorization: Bearer <sink-webhook-secret> # pro Senke in Sink Registry
X-Decision-Id: DEC-2026-0042
X-Payload-Hash: sha256:<final hash>
Idempotency-Key: <decision_id>
Content-Type: application/json
```
Der Senken-Adapter läuft **auf unserer Seite** (nicht im externen System), weil wir die Verifikation vor dem Push durchführen müssen. Er verwaltet pro Senke:
- Secret für `Authorization`-Header (im K8s-Secret),
- Retry-Backoff (3 Versuche, exponentiell),
- Rückmeldung an Agent C mit `sink_record_ref: "http:<url>:<externe-id-aus-response>"`.
Falls die Senke keine Response-ID liefert, generiert der Adapter eine aus `H(decision_id || endpoint_url)`.
### 7.4 Beispiel: Message-Queue (Senke `sink:nats-kafka-*`)
Für asynchrone Übergaben an interne Downstream-Systeme (Analytics, Replikation).
Der Senken-Adapter publiziert nach erfolgreicher Verifikation einen Event auf einem Topic. Payload enthält `workflow_id`, `decision_id`, `payload_hash` und (sofern erlaubt) den fachlichen Nutzinhalt. Idempotenz: Queue-Message-Deduplication-Key = `decision_id`. Senken-Belegnummer = Queue-Message-ID.
### 7.5 Beispiel: Dateiablage (Senke `sink:sftp-*`, `sink:s3-*`)
Für Export in Partnerumgebungen (SFTP) oder Cold-Storage.
Der Adapter legt eine Datei mit kanonischem Namen (`<workflow_id>_<decision_id>.json`) ab, signiert sie als DSSE und schreibt eine Index-Zeile in `sink_decision_log`. Dateipfad = `sink_record_ref`.
### 7.6 Welchen Adapter wo deployen?
| Senke | Adapter läuft bei uns | Adapter läuft im Zielsystem |
| -------------------- | --------------------- | --------------------------- |
| SAP S/4HANA | nein (dünner Agent C) | ja (ABAP-Endpoint + Validierungs-Webservice-Call) |
| Eigene DB-App | ja (`expense-db-adapter`) | — |
| HTTP-Webhook extern | ja (`webhook-adapter`) | — |
| Message-Queue | ja (`queue-adapter`) | — |
| Dateiablage | ja (`file-adapter`) | — |
| Legacy (Mail/EDI) | ja (adapter spezifisch) | — |
Daumenregel: Wenn das Zielsystem eigene Autorisierungs-/Audit-Funktionen hat (SAP), bringen wir dort einen dünnen Verifier unter. Wenn nicht, läuft der komplette Adapter in unserem Cluster.
## 8. Nachweisablage und Transparency Log
### 8.1 Scope und Retention
**Ziel:** kryptografisch belegen, dass die Agent-Kommunikation unverfälscht gelaufen ist, und diese Prüfung nachträglich zulassen. **Kein** rechtssicheres GoBD-Archiv.
**Retention Default: 1 Jahr.** Nach Ablauf werden Attestierungen, Decisions und zugehörige Schlüsselhistorie automatisiert gelöscht. Konfigurierbar pro Prozess-Typ *und pro Senken-Typ* (z.B. Webhook-Senken kürzer).
### 8.2 Datenklassen
| Daten | Speicherung |
| --------------------------------------- | ---------------------------------------- |
| Prozessstatus, Identitäten, Referenzen | PostgreSQL (`trust-postgres`) |
| Senken-Registrierung | PostgreSQL (`trust-postgres`) |
| Signierte Attestierungen (DSSE) | S3 Object Storage |
| Transparency-Log-Einträge + Proofs | Trillian + PostgreSQL |
| Reisekostenbelege und Geschäftsdaten | Verschlüsselt in S3 (separater Bucket, PII-Store) |
| Signatur- und Schlüsselhistorie | Agent Registry (Postgres), max 1 Jahr |
| Policy-Versionen | Git-versioniert, Hash im Evidence Store |
| Senken-Übergabenachweise | S3 + Index in `sink_decision_log` der jeweiligen Senke |
**Strikte Trennung PII vs. Nachweis.**
### 8.3 Warum Trillian + PostgreSQL (nicht Cassandra)
Ein Transparency Log ist ein append-only Merkle-Tree mit Inclusion Proofs und signierten Tree-Heads. Entscheidend: strikte Reihenfolge, starke Konsistenz, Inclusion Proofs in ms. Cassandra ist dafür ungeeignet (eventual consistency, Ordering nur per Partition). Trillian + PostgreSQL ist bewährt (Basis von sigstore rekor), passt in unsere bestehende Postgres-Infrastruktur und reicht für < 1 Mio Einträge/Jahr auf einer kleinen Instanz. Täglicher signierter Log-Root wird ins Backup-System gespiegelt.
## 9. Deployment in Kubernetes
```
identity/
pocket-id
agent-trust/
trust-api (Approval + Attestation + Agent Registry + Sink Registry + Verification)
signing-service (eigenes Deployment, enge Trust-Boundary)
policy-engine (OPA)
trust-postgres
trillian-log
transport/
nats (JetStream für Agent↔Agent)
paperclip/
paperclip
agent-travel
agent-sink-mapping
agent-sink-posting
sinks/
expense-db-adapter (für sink:expense-db-app)
expense-db-postgres
webhook-adapter (für sink:custom-webhook-*)
queue-adapter (für sink:nats-kafka-*)
file-adapter (für sink:sftp-*, sink:s3-*)
# SAP-Adapter läuft als ABAP im SAP-System (nicht in K8s)
openbao/
openbao
matrix/ (optional, Phase 5)
synapse
element-web
agent-matrix-gateway
```
### 9.1 Workload-Identitäts-Prüfung (doppelt)
Der Signing Service verifiziert für jeden Request **beide** Credentials:
1. **Pocket-ID-Access-Token** (Header `Authorization: Bearer`) — gegen Pocket-ID-JWKS; `sub = agent-<name>` muss zu einem registrierten Agent passen.
2. **Projected ServiceAccount-Token** (Header `X-Workload-Token`, Audience `signing-service.agent-trust`) — gegen K8s TokenReview; Namespace + ServiceAccount müssen zum Agent in der Registry passen.
Nur wenn beide auf denselben `agent_id` auflösen, wird signiert.
OpenBao Transit signiert Ed25519. Caveat: Transit kennt kein DSSE — der Signing Service berechnet die DSSE-PAE selbst und übergibt nur den Hash an Transit.
## 10. Implementierungsreihenfolge
**Phase 1 — End-to-End-Skelett (3–5 Wochen)**
Pocket ID als IdP, Approval Service mit Content Binding, ein Testagent mit Ed25519-Schlüssel in der Agent Registry, Signing Service mit doppelter Identitätsprüfung, Verification Service für zweistufige Kette, **eine triviale Senke** (`sink:dummy-sink` als Mock-Adapter in Go) für den vollständigen Durchlauf.
**Phase 2 — Agent Attestations in Breite**
Weitere Agenten, Schlüsselrotation, in-toto-Transformationsnachweise, OPA-Policies, Policy-Versionierung.
**Phase 3 — Replay-Schutz, Transparency Log, Retention**
Trillian-Deployment, Decision-Nonce-Mechanik, Decision-Replay-Tabellen, Retention-Cron-Jobs, PII-Store-Trennung.
**Phase 4 — Erste produktive Senke(n)**
Ein echter Senken-Adapter — Reihenfolge nach Business-Priorität:
- Variante A: **SAP S/4HANA** (ABAP-Endpunkt, serverseitige Mitarbeiterzuordnung, SM59-Rotationsprozess, Validierungs-Webservice).
- Variante B: **Eigene DB-Applikation** (Go-Adapter mit Postgres, einfacher zu bauen, geeignet als Pilot).
Beide Varianten nutzen denselben Senken-Vertrag aus 7.0. Welche zuerst gebaut wird, entscheidet die Produkt-Priorität.
**Phase 5 — Weitere Senken-Typen**
HTTP-Webhook-Adapter, Message-Queue-Adapter, File-Adapter — Baumuster steht nach Phase 4, inkrementell.
**Phase 6 — Matrix für Mensch-Chat (optional)**
Nur Mensch↔Agent-Chat, E2EE. Agent↔Agent läuft weiter über NATS/HTTPS.
## 11. Was bewusst anders ist als in V1.0
- SAP ist **eine** Senke, nicht das Ziel. Trust-Core ist senken-agnostisch, Senken sind Plug-ins mit festem Vertrag.
- Matrix ist **nicht** Standard-Transport — nur optionale Mensch-Chat-Schicht.
- Transparency Log ist eigene Komponente (Trillian), nicht nur Postgres-Tabelle.
- Signing Service ist eigenes Deployment ab Tag 1.
- PII-Store ist strikt getrennt vom Evidence Store.
- Keine Unveränderbarkeits-/GoBD-Garantien — Retention 1 Jahr.
- Pocket ID ist alleiniger IdP.
## 12. Empfohlener Kernstack (Zielbild)
| Identität | Pocket ID (Menschen + Agent-Clients) |
| ------------- | ------------------------------------------------------------------- |
| Workload-ID | K8s projected ServiceAccount Token + TokenReview |
| Signaturen | Ed25519 / DSSE, OpenBao Transit |
| Provenienz | in-toto v1, predicate-Namespace `basicstack.de/attestations/*/*` |
| Autorisierung | OPA (Rego) + Approval Service |
| Transparency | Trillian + PostgreSQL |
| Transport | HTTPS/mTLS + NATS JetStream (Agent↔Agent); Matrix optional (Mensch) |
| Agenten | Paperclip |
| **Senken** | **Plug-in-Architektur** — SAP S/4HANA (ABAP), eigene DB-Apps (Go-Adapter), HTTP-Webhooks, Message-Queues, Dateiablage |
| Evidence | PostgreSQL + S3 (Retention 1 Jahr) |
| PII | Separater verschlüsselter S3-Bucket, redaktionierbar |
**Wichtigste Empfehlung bleibt:** Zuerst in Phase 1 ein vollständiger signierter Testdurchlauf mit einer trivialen Mock-Senke. Erst in Phase 4 wird die erste echte Senke angebunden — ob SAP oder eine eigene DB-App entscheidet die Produkt-Priorität.