ProofedMesh/docs/architecture/v1.1.md

597 lines
32 KiB
Markdown
Raw Normal View History

# 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.