From 9f89f9fb6bc58ef7cc5e3575dfc8c947cd1d3fa4181dbc191acafbf65c4afef9 Mon Sep 17 00:00:00 2001 From: CTO Agent Date: Sun, 25 Oct 2026 14:11:34 +0000 Subject: [PATCH] =?UTF-8?q?docs(architecture):=20generalize=20sink=20conce?= =?UTF-8?q?pt=20=E2=80=94=20SAP=20is=20one=20of=20many=20sinks,=20add=20si?= =?UTF-8?q?nk=20adapter=20contract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/architecture/v1.1.md | 418 +++++++++++++++++++++++--------------- 1 file changed, 251 insertions(+), 167 deletions(-) diff --git a/docs/architecture/v1.1.md b/docs/architecture/v1.1.md index 6921735..2a2867f 100644 --- a/docs/architecture/v1.1.md +++ b/docs/architecture/v1.1.md @@ -1,47 +1,50 @@ -# Technische Zielarchitektur: Vertrauenswürdige Kommunikation zwischen Menschen, KI-Agenten und SAP +# 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) -**Änderungen gegenüber V1.0:** siehe Abschnitt 0 „Änderungen seit V1.0" +**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. Der empfangende Agent überprüft die vollständige Kette, bevor er eine Aktion ausführt.** +> **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 aus der ersten Review-Runde um: +Diese Revision setzt die Richtungsentscheidungen der ersten beiden Review-Runden um: -- **Scope Evidence Store (A.1/A.2):** kein rechtssicherer, kein WORM-/GoBD-Anspruch. Ziel ist der kryptografische Nachweis einer unverfälschten Kommunikation während eines **begrenzten Zeitraums** (Default: 1 Jahr). Danach dürfen Nachweise verworfen werden. -- **PII-Trennung (A.1):** verbindlich eingezogen. Attestierungen enthalten nur Hashes und Referenzen; personenbezogene Inhalte liegen in einem separaten, redaktionierbaren PII-Store. -- **IdP (A.3, A.5):** **Pocket ID** wird einziger IdP — für Menschen *und* Agenten. Begründung und Mechanik in Abschnitt 3a. Keycloak wird nicht eingeführt. -- **Content Binding (A.4):** eigener Abschnitt 4.0 als verbindliches UX-/Signatur-Protokoll. -- **Workload-Identität (A.5):** Agenten authentifizieren sich doppelt — Pocket-ID-`client_credentials`-Token *plus* projiziertes Kubernetes-ServiceAccount-Token. Signing Service verifiziert beide. -- **Schlüsselbund-Scope (A.6):** Historische Schlüssel werden nur für die aktive Nachweis-Retention vorgehalten (Default 1 Jahr). Rotation Default 90 Tage → max. ~5 Schlüssel pro Agent gleichzeitig gültig für die Verifikation. -- **Transparency Log (A.7, B.2):** PostgreSQL + Trillian-Merkle-Log statt Cassandra. Begründung in Abschnitt 8. -- **Replay-Schutz (A.8) und Fachliche Integritätsprüfung (A.9):** jetzt verbindliche Abschnitte 5.1 und 6.1. -- **ABAP-Token (A.10):** manuell im SAP-System hinterlegt (bestätigt). Rotationsprozess siehe Abschnitt 7. -- **Matrix (B.1):** verschoben in optionale Phase 5, nur für Mensch↔Agent-Chat. Agent↔Agent-Transport ist HTTPS/mTLS + NATS JetStream. -- **Signing Service (B.3):** eigenes Deployment von Tag 1. -- **Phase 1 (B.4):** End-to-End-Pfad mit einem Agenten als Abschluss. -- **predicateType-Namespace (B.5):** `https://basicstack.de/attestations//`. +- **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//`. +- **🆕 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 ERP-Aktionen. +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 | @@ -56,50 +59,51 @@ Logische Komponentenübersicht, keine direkte Netzwerktopologie. Freigabe- und A | 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) | -| SAP Connector | ABAP REST API / OData | Validierte Buchungsaufträge | +| **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 und Verification bleiben in **einem modularen Go-Dienst** zusammengefasst. Separate APIs und Module bleiben erhalten, aber der Betrieb vieler kleiner Microservices wird vermieden. **Signing Service läuft als eigenes Deployment** mit separater Trust-Boundary. +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: warum Pocket ID reicht +### 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 decken wir beide Welten: +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 normalen Authorization-Code-Flow an. -- **Agenten** werden in Pocket ID als eigene OIDC-Clients registriert (ein Client pro Agent, z.B. `agent-travel`, `agent-sap-mapping`, `agent-sap-posting`). Jeder Client erhält eine `client_id` + ein `client_secret`, das im zugehörigen Kubernetes-Namespace als Secret hinterlegt wird. Der Agent holt sich daraus ein kurzlebiges Access-Token per `client_credentials`. -- **SAP** (bzw. die ABAP-Validierungs-Webservice-Logik aus Abschnitt 7) kann denselben Pocket-ID-JWKS-Endpunkt nutzen, um ein von Agent C präsentiertes Token zu verifizieren — ohne Federation, ohne weiteren IdP. - -Keycloak wird **nicht** eingeführt. Das spart einen betriebskritischen Dienst. +- **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 (z.B. Reisekostenabrechnung) 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) liegt im separaten PII-Store. +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 +### Vertrauens- und Verarbeitungskette (allgemein, am Beispiel Reisekosten → beliebige Senke) -1. **Benutzerfreigabe** — Approval Service bindet die Signatur an den Hash des Inhalts, den der Benutzer tatsächlich im Browser gesehen und 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 die ursprüngliche Freigabe. -3. **Agent B: SAP-Mapping** — Agent B signiert seine eigene Attestierung und referenziert die Ausgabe von Agent A. -4. **Agent C: SAP-Buchung** — Agent C überprüft die komplette Kette und erzeugt nach erfolgreicher Buchung einen signierten Buchungsnachweis. +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 -Das Risiko: der Browser könnte dem Benutzer etwas anderes zeigen als dem Approval Service. +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 dessen SHA-256-Hash `H_content` und speichert `(session_id, H_content, salt)` zusammen mit einem einmaligen `challenge_nonce`. -2. Browser zeigt den Inhalt **und** eine aus `H_content + challenge_nonce` abgeleitete, menschenlesbare **Prüfziffer** (z.B. 6 Zeichen Base32) als visuelles Fingerprinting an. -3. Benutzer bestätigt per Passkey (WebAuthn-Signatur über `challenge_nonce || H_content`). Die WebAuthn-Challenge enthält `H_content` als Teil der Assertion-Daten — der Hardware-Authenticator signiert damit explizit das, was der Server und (per Prüfziffer) der Benutzer gesehen haben. -4. Approval Service verifiziert die WebAuthn-Assertion gegen den registrierten Passkey, prüft, dass die Session-Daten unverändert sind, und erzeugt erst dann die DSSE-signierte Approval-Attestierung. +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 dem Benutzer eine andere Zahl zeigt, am visuellen Prüfziffer-Vergleich *oder* am WebAuthn-Signaturinhalt. +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", @@ -113,10 +117,7 @@ Damit scheitert ein kompromittierter Browser, der dem Benutzer eine andere Zahl "predicate": { "workflow_id": "WF-0042", "approval_id": "APR-0042", - "schema": { - "name": "expense.canonical", - "version": "1.0.0" - }, + "schema": { "name": "expense.canonical", "version": "1.0.0" }, "issuer": "approval-service", "user": { "iss": "https://auth.basicstack.de", @@ -128,8 +129,9 @@ Damit scheitert ein kompromittierter Browser, der dem Benutzer eine andere Zahl "verification_code": "F3K-9MQ" }, "action": "expense.submit", - "audience": "sap-expense-workflow", - "allowed_transformations": ["expense.normalize", "expense.map_to_sap"], + "audience": "sink:sap-s4hana-prod", + "allowed_transformations": ["expense.normalize", "expense.map_to_sink"], + "allowed_sinks": ["sink:sap-s4hana-prod", "sink:expense-db-app"], "nonce": "", "issued_at": "2026-10-25T10:30:00Z", "expires_at": "2026-11-01T00:00:00Z" @@ -137,7 +139,7 @@ Damit scheitert ein kompromittierter Browser, der dem Benutzer eine andere Zahl } ``` -Der Approval Service darf dieses Dokument nur erzeugen, wenn die WebAuthn-Signatur über dem Content-Hash gültig ist. Ein Agent darf diesen Endpunkt nicht mit einer frei angegebenen Benutzer-ID aufrufen. +Neu: `allowed_sinks` erlaubt in der Freigabe explizit mehrere Zielsenken (z.B. Dual-Write SAP + eigene DB). ### 4.2 Signatur mit DSSE @@ -151,32 +153,33 @@ Der Approval Service darf dieses Dokument nur erzeugen, wenn die WebAuthn-Signat } ``` -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 das in der Agent Registry zum `issued_at` gültige öffentliche Schlüsselmaterial. +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) +### 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/sap-payload", + "name": "expense/EXP-2026-0042/sink-payload", "digest": { "sha256": "" } } ], "predicateType": "https://basicstack.de/attestations/transform/v1", "predicate": { "workflow_id": "WF-0042", - "actor": "agent-sap-mapping", - "operation": "expense.map_to_sap", + "actor": "agent-sink-mapping", + "operation": "expense.map_to_sink", + "sink_target": "sink:sap-s4hana-prod", "materials": [ { "name": "expense/EXP-2026-0042/normalized", "sha256": "" } ], "parent_attestation": "sha256:", "approval_ref": "APR-0042", "schema": { - "input": { "name": "expense.canonical", "version": "1.0.0" }, - "output": { "name": "sap.expense.payload", "version": "1.2.0" } + "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" @@ -184,37 +187,35 @@ DSSE's Pre-Authentication Encoding (PAE) signiert Nutzlast und Inhaltstyp gemein } ``` -Agent B signiert diese Attestierung mit seinem eigenen Schlüssel. +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 (unsere Postgres-Tabelle):** die Identität plus Signatur-Schlüsselhistorie. - -Schema (vereinfacht): +- **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, + 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 + display_name TEXT NOT NULL, + allowed_actions TEXT[] NOT NULL, + created_at TIMESTAMPTZ NOT NULL ); CREATE TABLE agent_keys ( - key_id TEXT PRIMARY KEY, -- z.B. "agent-travel-2026-q4" + key_id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents(agent_id), - public_key_pem TEXT NOT NULL, -- Ed25519 + 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 -- gesetzt wenn außerhalb Retention + archived_at TIMESTAMPTZ ); CREATE INDEX idx_agent_keys_lookup @@ -222,13 +223,34 @@ CREATE INDEX idx_agent_keys_lookup WHERE archived_at IS NULL; ``` -**Validierung alter Attestierungen** nutzt den Schlüssel, der zum `issued_at` der Attestierung galt (`valid_from ≤ issued_at < valid_to`, nicht `revoked_at` vor `issued_at`). Das ist das TUF-Pattern. +**Validierung alter Attestierungen** nutzt den Schlüssel, der zur `issued_at` galt (TUF-Pattern). -**Retention und Performance.** Default-Rotation **90 Tage**, Nachweis-Retention **1 Jahr** → pro Agent max. ~5 nicht-archivierte Schlüssel. Beim Überschreiten der Retention wird der Schlüssel auf `archived_at` gesetzt; alte Attestierungen, die noch im Evidence Store liegen, aber über diese Grenze hinaus zu validieren wären, werden explizit abgelehnt (`decision: expired_retention`). Damit bleibt der aktive Keyring klein und die Validierung konstant schnell — auch bei 100+ Agenten. +**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. -## 5. Verifikation beim empfangenden Agenten +### 4.5 Sink Registry -Der SAP-Agent darf sich nicht auf die mitgesendeten IDs verlassen. Er muss die gesamte Nachweiskette prüfen. +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 @@ -240,24 +262,28 @@ Content-Type: application/json { "workflow_id": "WF-0042", "artifact_digest": "sha256:", - "requested_action": "expense.post_to_sap", - "target": "sap-s4hana-prod" + "requested_action": "expense.post", + "target": "sink:sap-s4hana-prod" } ``` +Prüfungen: + | Prüfung | Fehlerfall | | -------------------------------------------------------------------- | --------------------- | -| Signatur der Benutzerfreigabe gültig (Schlüssel zum `issued_at` gültig)? | Ablehnen | +| 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 | -| SAP-Nutzlast entspricht freigegebener Geschäftssemantik (OPA)? | Ablehnen | -| `payload_hash` bereits für `target` gebucht? | Keine erneute Buchung | +| Nutzlast entspricht freigegebener Geschäftssemantik (OPA)? | Ablehnen | +| `payload_hash` bereits für `target` übergeben? | Keine erneute Übergabe | Erfolgsantwort: @@ -266,41 +292,40 @@ Erfolgsantwort: "valid": true, "workflow_id": "WF-0042", "original_user": { "issuer": "https://auth.basicstack.de", "subject": "user-12345" }, - "actors": ["agent-travel", "agent-sap-mapping"], - "authorized_action": "expense.post_to_sap", + "actors": ["agent-travel", "agent-sink-mapping"], + "authorized_action": "expense.post", "artifact_digest": "sha256:", "decision_id": "DEC-2026-0042", "decision_nonce": "", - "bound_to_agent": "agent-sap-posting", - "bound_to_target": "sap-s4hana-prod", + "bound_to_agent": "agent-sink-posting", + "bound_to_target": "sink:sap-s4hana-prod", "expires_at": "2026-10-25T10:36:00Z" } ``` -Diese Decision ist eine kurzlebige, an Agent C + Payload-Hash + Target gebundene Ausführungsberechtigung. - ### 5.1 Replay-Schutz (verbindlich) -- `decision_id` enthält einen eigenen `decision_nonce`, Lebensdauer **5 Minuten**. +- `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 Doppelbuchungs-Check. -- SAP-Connector führt eine **eigene** Replay-Tabelle (`sap_decision_log`) mit `UNIQUE (decision_id)`. Beide Prüfungen sind unabhängig voneinander — Defence in Depth. -- Doppelbuchung wird gegen `payload_hash` (nicht nur `workflow_id`) geprüft, damit minimal veränderte Nutzlasten nicht durchrutschen. +- 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: +Beispiel-Regeln (bewusst nicht ERP-spezifisch): -- Agent A darf Belege extrahieren und Reisekosten strukturieren. -- Agent B darf Kontierungsinformationen ergänzen und SAP-Felder zuordnen. +- 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 Reisekostenabrechnungen buchen. +- 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 verändert. Deshalb binden wir die Freigabe an ein **versioniertes kanonisches Schema**: +Ein Hashvergleich allein reicht nicht, weil sich beim Mapping das technische Format ändert. Deshalb binden wir die Freigabe an ein **versioniertes kanonisches Schema**: ```json { @@ -317,17 +342,34 @@ Ein Hashvergleich allein reicht nicht, weil sich beim Mapping das technische For Verbindlich: -- Schemas werden als JSON Schema in `schemas/` im Repo versioniert. +- 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. +- 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. SAP S/4HANA Integration +## 7. Senken-Integration (generalisiert) -Für die S/4HANA-2023-Umgebung ist eine ABAP-Integrationsschicht vorgesehen, statt Agent C direkten Zugriff auf Buchungsfunktionen zu geben. +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. -Fluss: `Agent C` → `ABAP REST Endpoint` (Authentifizierung · Freigabenachweis · Idempotenz) → `SAP-Anwendungslogik` (Mitarbeiterzuordnung · fachliche Prüfungen · Buchung). +### 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 @@ -346,96 +388,125 @@ Content-Type: application/json } ``` -Zusätzlich stellt ProofedMesh einen **Validierungs-Webservice** bereit, den das ABAP vor der Buchung aufruft: +Vor der Buchung ruft ABAP den Validierungs-Webservice: ``` POST /api/v1/decisions/{decision_id}/replay-check ``` -Rückgabe: ob die Decision noch gültig, nicht verbraucht, an Agent C gebunden und für dieses `target` autorisiert ist. Damit braucht SAP keine eigene Krypto-Logik. +Rückgabe: ob Decision noch gültig, nicht verbraucht, an Agent C und Target gebunden. -**Zwei unabhängige Prüfungen** in SAP: +**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. -- **Technische Identität:** Der Aufruf kommt vom autorisierten SAP-Agenten (Bearer-Token-Prüfung). -- **Fachliche Identität:** Der freigebende Benutzer wird über eine serverseitig gepflegte Zuordnung der korrekten SAP-Personalnummer zugeordnet. Eine vom Agenten gelieferte Personalnummer darf diese Zuordnung nicht überschreiben. +**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)`. -**sap-agent-token** wird manuell im SAP-System hinterlegt. Rotationsprozess: +### 7.2 Beispiel: Eigene Datenbankapplikation (Senke `sink:expense-db-app`) -1. Signing Service oder Operator generiert neues Token (JWT, 90-Tage-Gültigkeit) und signiert es mit dem Service-Schlüssel von Agent C. -2. Operator trägt neues Token in SAP ein (Transaktion `SM59` oder äquivalent, abhängig von der konkreten SAP-Konfiguration). -3. Agent C erhält das neue Token über ein K8s-Secret; alte Secrets werden nach einer Übergangszeit entfernt. -4. SAP-Validierung akzeptiert alte und neue Token, solange sie signiert sind — die Agent-Identität wird aus dem Token-`sub` gelesen, nicht aus dem Token-Material selbst. +Minimale Senken-Variante: eine selbst gebaute Postgres-App (eigener Microservice). -**Welche SAP-Buchungsschnittstelle** verwendet wird (SAP Travel Management, Concur, kundeneigener FI-Prozess), bleibt in einem späteren ADR offen — architektonisch ist das austauschbar. +Fluss: `Agent C` → HTTPS/mTLS → `expense-db-adapter` (Go-Service) → `expense-postgres`. -### SAP-Auditdaten +``` +POST /api/v1/expense/submit +Authorization: Bearer # Pocket-ID-Access-Token +Content-Type: application/json +``` -Zusätzlich zum ERP-Beleg: Tabelle `ZAGENT_AUDIT`. +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/`) an Agent C zurück. -| Feld | Inhalt | -| --------------------- | ---------------------------------- | -| WORKFLOW_ID | Eindeutige Prozess-ID | -| APPROVAL_ID | Benutzerfreigabe | -| USER_SUB | Externe Benutzeridentität | -| PERNR | Zugeordnete Personalnummer | -| AGENT_ID | Buchender Agent | -| PAYLOAD_HASH | SHA-256 der Buchungsdaten | -| VERIFICATION_ID | Verifikationsentscheidung | -| DECISION_ID | Replay-Check-Referenz | -| BUKRS / BELNR / GJAHR | FI-Belegreferenz | -| CREATED_AT | Buchungszeitpunkt | +Idempotenz: `UNIQUE (decision_id)` und `UNIQUE (payload_hash)` in `sink_decision_log`. Business-Tabelle hat `UNIQUE (workflow_id)`. -Idempotenz: `UNIQUE (WORKFLOW_ID)` *und* `UNIQUE (DECISION_ID)`. Bei N fehlgeschlagenen Buchungsversuchen landet der Vorgang in einer SAP-Operator-Queue zur manuellen Prüfung — kein stiller Datenverlust. +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 +Authorization: Bearer # pro Senke in Sink Registry +X-Decision-Id: DEC-2026-0042 +X-Payload-Hash: sha256: +Idempotency-Key: +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::"`. + +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 (`_.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 auch nachträglich zulassen. **Kein** rechtssicheres GoBD-Archiv. +**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 (`archived_at` + Cron-Job). Konfigurierbar pro Prozess-Typ. +**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**) | +| 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:** Der PII-Store (Belege, Namen, Kostenstellen) ist unabhängig vom Evidence Store löschbar. Löscht ein Benutzer per DSGVO-Antrag seine Daten, verschwinden die Belege; die Hashes und Signaturen in Evidence Store und Transparency Log bleiben erhalten, verifizieren aber dann nur noch „diese Kette existierte", nicht mehr „ihr Inhalt war X". +**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 periodisch signiertem Tree-Root (Signed Tree Head). Entscheidend sind: - -- **Strikte Reihenfolge** aller Einträge (eine lineare Sequenznummer). -- **Starke Konsistenz** zwischen Logeintrag und Merkle-Root — der Root muss nach jedem Append reproduzierbar berechnet werden können. -- **Inclusion Proofs** auf Anfrage, idealerweise in ms. - -Cassandra ist für diese Zugriffsmuster architektonisch ungeeignet: eventual consistency, ordering nur per Partition, keine serialisierbare globale Sequenz. Es wäre technisch möglich, aber mit erheblichem Over-Engineering (externer Sequencer, Lock-Dienst) — und würde sigstore's Entscheidung widersprechen. - -**Empfehlung:** [Google Trillian](https://github.com/google/trillian) als Transparency-Log-Engine (ist die Grundlage von sigstore rekor), gestützt auf PostgreSQL — das ist unsere ohnehin vorhandene Infrastruktur. Für unser erwartetes Volumen (< 1 Mio. Einträge/Jahr, Single-Tenant) läuft das auf einer kleinen Instanz. Der Log-Root wird täglich signiert und ins Backup-System gespiegelt — damit ist eine nachträgliche Manipulation durch den DB-Admin erkennbar. - -Falls Trillian als Dependency zu schwer wirkt, Alternative: eine eigene minimale Append-Only-Postgres-Tabelle mit stündlich berechnetem Merkle-Root und Export des Roots als signiertes JSON — deutlich weniger Funktionalität (keine Inclusion Proofs auf Anfrage), aber ausreichend für unseren Scope. +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 -Namespaces und Dienste: - ``` identity/ - pocket-id (bestehend) + pocket-id agent-trust/ - trust-api (Approval + Attestation + Registry + Verification) + trust-api (Approval + Attestation + Agent Registry + Sink Registry + Verification) signing-service (eigenes Deployment, enge Trust-Boundary) policy-engine (OPA) trust-postgres - trillian-log (Transparency Log) + trillian-log transport/ nats (JetStream für Agent↔Agent) @@ -443,11 +514,19 @@ transport/ paperclip/ paperclip agent-travel - agent-sap-mapping - agent-sap-posting + 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 (Transit Engine für Signaturschlüssel) + openbao matrix/ (optional, Phase 5) synapse @@ -455,48 +534,53 @@ matrix/ (optional, Phase 5) agent-matrix-gateway ``` -**Signing Service**: eigenes Deployment, dediziertes Pocket-ID-Client-Secret, eigener ServiceAccount, NetworkPolicy erlaubt eingehend nur die Agent-Namespaces. Agenten rufen den Signing-Endpunkt auf; private Schlüssel verlassen OpenBao nie. - ### 9.1 Workload-Identitäts-Prüfung (doppelt) -Der Signing Service verifiziert für jeden eingehenden Request **beide** Credentials: +Der Signing Service verifiziert für jeden Request **beide** Credentials: -1. **Pocket-ID-Access-Token** (Header `Authorization: Bearer `) — gegen Pocket-ID-JWKS validiert; `sub` = `agent-` muss zu einem registrierten Agent in der Agent Registry gehören. -2. **Projected ServiceAccount-Token** (Header `X-Workload-Token: `, Audience `signing-service.agent-trust`) — gegen Kubernetes-TokenReview-API validiert; Namespace + ServiceAccount müssen zum Agent in der Agent Registry passen. +1. **Pocket-ID-Access-Token** (Header `Authorization: Bearer`) — gegen Pocket-ID-JWKS; `sub = agent-` 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 Prüfungen zum selben `agent_id` auflösen, wird signiert. Damit ist weder ein geleaktes Pocket-ID-Secret allein noch ein kompromittierter Pod außerhalb des erwarteten Namespaces ausreichend. +Nur wenn beide auf denselben `agent_id` auflösen, wird signiert. -OpenBao's Transit Engine signiert mit Ed25519. **Caveat:** Transit kennt kein DSSE — der Signing Service berechnet die DSSE-Pre-Authentication-Encoding (PAE) selbst und übergibt nur den Hash an Transit. Backup via Transit-Export-Policy und Snapshot der OpenBao-Instanz. +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 bis 5 Wochen)** -Pocket ID als IdP (Mensch und Agent-Client), 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 die zweistufige Kette (Approval → Agent → Simulierte Senke). Deliverable: ein signierter Durchlauf vom Login bis zur simulierten SAP-Senke. +**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, Materialien-Hash-Kette, OPA-Policies in Rego, Policy-Versionierung. +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-Tabelle, Retention-Cron-Job, PII-Store-Trennung. +Trillian-Deployment, Decision-Nonce-Mechanik, Decision-Replay-Tabellen, Retention-Cron-Jobs, PII-Store-Trennung. -**Phase 4 — SAP-Integration** -ABAP-Endpunkt, serverseitige Mitarbeiterzuordnung, SAP-seitige Replay-Prüfung über Validierungs-Webservice, Operator-Queue. +**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 — Matrix für Mensch-Chat (optional)** -Nur Mensch↔Agent-Chat, E2EE. Agent↔Agent läuft weiterhin über NATS/HTTPS. +**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 -- Matrix ist **nicht** Standard-Transport — nur optionale Mensch-Chat-Schicht in Phase 5. Intern NATS JetStream + HTTPS/mTLS. -- Transparency Log ist eine eigene Komponente, nicht nur „Postgres-Tabelle". +- 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, dann Löschung. -- Pocket ID ist alleiniger IdP — Keycloak entfällt. +- Keine Unveränderbarkeits-/GoBD-Garantien — Retention 1 Jahr. +- Pocket ID ist alleiniger IdP. ## 12. Empfohlener Kernstack (Zielbild) -| Identität | **Pocket ID** (Menschen + Agent-Clients) | +| Identität | Pocket ID (Menschen + Agent-Clients) | | ------------- | ------------------------------------------------------------------- | | Workload-ID | K8s projected ServiceAccount Token + TokenReview | | Signaturen | Ed25519 / DSSE, OpenBao Transit | @@ -505,8 +589,8 @@ Nur Mensch↔Agent-Chat, E2EE. Agent↔Agent läuft weiterhin über NATS/HTTPS. | Transparency | Trillian + PostgreSQL | | Transport | HTTPS/mTLS + NATS JetStream (Agent↔Agent); Matrix optional (Mensch) | | Agenten | Paperclip | -| ERP | SAP S/4HANA / ABAP REST + Validierungs-Webservice | +| **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 Testprozess von der Benutzerfreigabe bis zur simulierten SAP-Senke, mit genau einem Agenten. Matrix und ADRs für Schema/OPA-Deep-Dive folgen danach. +**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.