Zum Inhalt springen

Event-Bus-Referenz (AsyncAPI 3.0)

Connectors heben normalisierte Beobachtungen auf den internen Event-Bus der Engine als Events; Module und Output-Connectors abonnieren nach Event-Typ und reagieren — ohne dass eines von ihnen ein anderes importiert. Diese Seite ist dieser Contract, ausgedrückt als AsyncAPI 3.0 — das asynchrone Gegenstück zur REST-API-Referenz.

  • Transport. Der v1-Standard ist ein In-Process-Go-Channel-Bus innerhalb des einzelnen olivares-Binarys. Das Bus-Interface exponiert keinen Channel, sodass eine verteilte Implementierung (NATS) für Multi-Host-Deployments eingeschoben werden kann, ohne einen einzigen Subscriber zu ändern. NATS ist das geplante verteilte Binding, nicht erforderlich für den Standard.
  • Subscription. Ein Subscriber registriert einen Handler, gefiltert nach einem Set von Event- Typen; ein leeres Set bedeutet jedes Event. Der Bus besitzt die Goroutine, die den Handler ausführt.
  • Delivery. Asynchron und at-least-once: jeder Subscriber hat seine eigene gepufferte Queue, geleert von einer dedizierten Goroutine; ein langsamer Subscriber wendet Backpressure an; ein Handler-Panic wird isoliert. Consumer deduplizieren über den Natural-Key-Timestamp nach einem Connector-Neustart.
  • Minimal-Data. Jedes Event trägt den Fakt, niemals rohe Payloads, Secrets oder PII. Eine Kante ist (origin → resource, R/RW); ein Finding trägt einen Hash des geschwärzten Details, niemals das Detail.
  • Durable Intake. audit.recorded und die dauerhaften work.*-Typen laufen niemals über den prozesslokalen Bus. Ihre Source-Outbox liefert eine stabile ID und bestätigt erst, nachdem Eventing das Event persistiert hat. Ein identisches Replay ist ein No-op; die Wiederverwendung derselben ID für einen anderen Typ, eine andere Source, Zeit oder Payload wird abgelehnt.

Jedes Event teilt sich den immutablen Event-Envelope; die Beobachtung reitet in Payload.

| Feld | Typ | Bedeutung | |---|---|---| | ID | string | Eindeutige Event-ID, für Bus-Traffic von der Engine vergeben und bei einer dauerhaften Source vor dem Intake erforderlich. | | Type | string | Der Diskriminator — eine der unten aufgeführten Channel-Adressen. | | Tenant | string | Der ursprüngliche Tenant als String-Referenz; die Engine löst ihn intern auf. | | Source | string | Name der Komponente (Connector/Modul), die es emittierte. | | Time | timestamp | Wann der zugrunde liegende Fakt eintrat, in der Uhr des Connectors. | | Payload | object | Der Fakt — eines der Payload-Schemas unten. |

Der Katalog kombiniert versiegelte First-Party-Observations, moduldefinierte Live-Bus- Events und die unten aufgeführten Channels, die ausschließlich über Durable Intake laufen. Jeder Typ trägt seine eigene Stabilitätsstufe, getrieben aus dem In-Code-Katalog (siehe API-Stabilität: stable = 24-monatiges Deprecation→Sunset-Fenster, beta = 12-monatig, bindend ab GA; ein Beta-Payload kann weiterhin Felder gewinnen, niemals stillschweigend verlieren).

| Channel (Type) | Payload | Zweck | Transport | Stabilität | |---|---|---|---|---| | edge.observed | EdgeObservation | ein Origin berührte eine Resource (R/RW) — das Rückgrat der Access Map | typed gRPC oneof | stable | | cost.sampled | CostSample | ein Modell-/Provider-Usage-Kostenfakt | typed gRPC oneof | stable | | finding.reported | FindingReport | ein Guardrail-/Red-Team-/Forensik-Finding | typed gRPC oneof | stable | | guardrail.observed | ObservedText | ein geschwärzter Auszug beobachteten Agent-Texts (Detective-Input) | JSON-Fallback | beta | | approval.requested | ApprovalRequest | eine ausstehende Approval wurde eröffnet und wartet auf Entscheidung | JSON-Fallback | beta | | policy.changed | PolicyChange | eine Governance-Policy wurde erstellt, aktualisiert oder gelöscht | JSON-Fallback | beta | | metric.sampled | MetricSample | Nutzungs-/Produktivitätsmetrik; das Subject kann eine Entwicklerreferenz sein | typed gRPC oneof | beta | | approval.resolved | ApprovalResolution | eine ausstehende Approval erreichte ein terminales Ergebnis | JSON-Fallback | beta | | workflow.signal | WorkflowSignal | ein eventing-emit-Schritt eines DAG-Workflows lief | JSON-Fallback | beta | | work.item.created | WorkEventFact | Durable-WorkItem-Erstellungsfakt | Durable Intake | beta | | work.item.transitioned | WorkEventFact | Durable-WorkItem-Update, -Archivierung oder governed Zustandsübergang | Durable Intake | beta | | work.owner.changed | WorkEventFact | kanonischer Owner oder Ownership-Epoch wurde geändert | Durable Intake | beta | | work.dependency.changed | WorkEventFact | Dependency wurde hinzugefügt, reaktiviert oder tombstoned | Durable Intake | beta | | work.acceptance.changed | WorkEventFact | Akzeptanzkriterium wurde erstellt, aktualisiert, bewertet oder erlassen | Durable Intake | beta | | work.message.available | DirectNoticeAvailableV1 oder WorkflowMessageCarrierV1 | DirectNotice- oder Workflow-Work-Task-Message-Carrier wurde verfügbar | Durable Intake | beta | | work.message.acknowledged | DirectNoticeAcknowledgedV1 | eine Delivery erhielt einen expliziten Ack, pünktlich oder verspätet | Durable Intake | beta | | work.message.retracted | MessageLifecycleV1 | eine Message wurde zurückgezogen | Durable Intake | beta | | work.message.expired | MessageLifecycleV1 | eine Message erreichte ihre Ablaufgrenze | Durable Intake | beta | | work.message.overdue | MessageLifecycleV1 | eine Message überschritt ihre Bestätigungsfrist | Durable Intake | beta | | work.message.rerouted | MessageDerivedV1 | ein governed Reroute leitete einen neuen Message-Carrier ab | Durable Intake | beta | | work.message.escalated | MessageDerivedV1 | eine überfällige Message erzeugte eine begrenzte Eskalation | Durable Intake | beta | | work.protocol.reply.available | WorkflowMessageCarrierV1 | eine authentifizierte Protokollantwort wurde zu einem begrenzten lokalen Message-Carrier | Durable Intake | beta | | work.protocol.message.received | WorkflowMessageCarrierV1 | eine authentifizierte eingehende Protokoll-Message wurde zu einem begrenzten lokalen Message-Carrier | Durable Intake | beta | | work.handoff.carrier.available | WorkflowMessageCarrierV1 | ein Workflow erstellte den Message-/Delivery-Carrier für einen künftigen Handoff | Durable Intake | beta | | work.decision.recorded | WorkEventFact | Append-only-Decision und aktuelle Head-Projektion wurden aufgezeichnet | Durable Intake | beta | | work.decision.request.responded | DecisionRequestEvent | eine DecisionRequest erhielt eine explizite governed Antwort | Durable Intake | beta | | work.decision.request.expired | DecisionRequestEvent | eine DecisionRequest überschritt ihre Frist | Durable Intake | beta | | work.handoff.offered | HandoffEventV1 | ein governed Handoff wurde angeboten | Durable Intake | beta | | work.handoff.accepted | HandoffEventV1 | das Handoff-Ziel nahm das Angebot an | Durable Intake | beta | | work.handoff.rejected | HandoffEventV1 | das Handoff-Ziel lehnte das Angebot ab | Durable Intake | beta | | work.handoff.withdrawn | HandoffEventV1 | der Handoff-Owner zog das Angebot zurück | Durable Intake | beta | | work.handoff.expired | HandoffEventV1 | ein Handoff überschritt seine Bestätigungsfrist | Durable Intake | beta | | work.lease.acquired | WorkEventFact | Lease wurde erworben, übernommen oder erneuert; Renew behält seinen Fence | Durable Intake | beta | | work.lease.ended | WorkEventFact | Lease wurde freigegeben, lief ab oder wurde widerrufen; alte Generation invalidiert | Durable Intake | beta | | work.binding.reserved | ProtocolBindingEvent | Protokollbindung und gefencete WorkItem-Autorität vor Übertragung reserviert | Durable Intake | beta | | work.binding.observed | ProtocolBindingEvent | Protokoll-Observation erzeugte CLEAN oder BROKEN | Durable Intake | beta | | work.binding.ambiguous | ProtocolBindingEvent | Protokoll-Observation blieb ausdrücklich UNKNOWN | Durable Intake | beta | | work.binding.cancel_requested | ProtocolBindingEvent | Abbruchabsicht wurde vor dem Remote-Side-Effect dauerhaft beansprucht | Durable Intake | beta | | audit.recorded | AuditRecord | versiegelter Audit-Ledger-Datensatz an ein SIEM — niemals auf dem Bus | Durable Intake | stable |

Minimal-Data: nur Identifikatoren und die Zugriffsklassifizierung.

| Feld | Typ | Hinweise | |---|---|---| | OriginKind | enum | agent | identity | session | | OriginRef | string | die natürliche Referenz des Connectors für das Origin | | ResourceKind | string | z. B. postgres.table, s3.bucket, http.api | | ResourceRef | string | z. B. public.customers, arn:aws:s3:::bucket | | Mode | enum | unknown | read | write | readwrite | | Source | enum | otel | mcp_annotation | pg_audit | cloudtrail | ebpf | policy | a2a | | Confidence | enum | attributed | approximate | | ToolRef | string | optionales Tool/Operation, das den Zugriff durchführte | | ObservedAt | timestamp | der Natural-Key-Timestamp, über den Consumer deduplizieren |

mcp_annotation wird als untrusted behandelt (bestätigt, niemals allein vertraut); unknown-Modus und approximate-Konfidenz sind explizit, sodass das Produkt niemals Gewissheit fabriziert.

Geld ist Integer-Micro-USD (Millionstel eines Dollars). Die Felder unterhalb der ersten sieben sind eine additive, provider-neutrale Erweiterung, ausgerichtet an OpenTelemetry gen_ai.* und FOCUS; Null/leer bedeutet „nicht gemeldet“, niemals „null“.

| Feld | Typ | Hinweise | |---|---|---| | ProviderRef, ModelRef | string | natürliche Provider-/Modell-Referenzen | | SessionRef | string | optionale Session-Anbindung | | InputTokens | int64 | GESAMT-Input (der Cache-Split unten ist eine Aufschlüsselung davon) | | OutputTokens | int64 | Output-Anzahl | | CostMicroUSD | int64 | Kosten in Micro-USD | | OccurredAt | timestamp | wann die Usage eintrat | | CacheReadTokens | int64 | Cache-Hit-Input-Tokens | | CacheCreation1hTokens, CacheCreation5mTokens | int64 | Cache-Write-Tokens nach TTL | | WorkspaceRef | string | Billing-Workspace/-Projekt | | APIKeyRef | string | maskierte Key-/Service-Account-Referenz, niemals das Secret | | Actor | string | der Principal, der die Kosten verursachte (Chargeback-„wer“) | | ServiceTier, ContextWindow, InferenceGeo | string | Provider-Vokabular (Tier / Context-Band / Residency) | | Gateway | enum | direct | bedrock-mantle | bedrock-legacy | vertex | foundry | claude-platform-aws (offener String) | | Provenance | enum | estimated | billed — leer wird als estimated behandelt | | CostType | string | Non-Token-Server-Tool-Charge-Klasse (leer = gewöhnliche Token-Kosten) |

| Feld | Typ | Hinweise | |---|---|---| | Kind | string | z. B. guardrail, redteam, forensic | | Severity | enum | info | low | medium | high | critical | | SubjectKind, SubjectRef | string | worüber das Finding ist | | Title | string | kurze, nicht-sensible Zusammenfassung, sicher anzeigbar | | DetailHash | string | Hex-SHA-256 des geschwärzten Details; das rohe Detail wird niemals übertragen oder gespeichert | | OccurredAt | timestamp | wann das Finding erzeugt wurde | | OWASPLLM, OWASPASI, ATLAS | string[] | Framework-Referenzen (OWASP LLM Top 10, OWASP Agentic Top 10, MITRE ATLAS); ein Finding kann mehreren gleichzeitig zugeordnet sein |

Ein begrenzter, bereits geschwärzter Auszug beobachteten Agent-Texts. Der Producer muss Secrets/PII vor dem Emittieren schwärzen; der Consumer klemmt ihn defensiv erneut.

| Feld | Typ | Hinweise | |---|---|---| | Surface | enum | input | output | tool_args | | Text | string | der geschwärzte, begrenzte Auszug, den die Detektoren inspizieren | | AgentRef, SessionRef, ResourceRef | string | nicht-sensible Kontext-Referenzen (jede kann leer sein) |

Minimal-Data: nur Identifikatoren und die Entscheidungsparameter der Approval. Es trägt bewusst weder den Freitext-Grund des Anfordernden noch die Subject-Referenz; ein für governance:approval:read autorisierter Consumer holt die vollständige Approval über ApprovalID.

| Feld | Typ | Hinweise | |---|---|---| | ApprovalID | string | die ID der Approval — die Referenz zum Holen, Entscheiden oder Beobachten der Anfrage | | Action | string | die angeforderte Action (ein begrenzter Kurz-Identifikator) | | SubjectKind | string | die Art des Subjects, das die Action zum Ziel hat (die Referenz des Subjects wird bewusst nicht getragen) | | RiskTier | string | die Risikoklassifizierung, unter der die Anfrage eröffnet wurde (z. B. critical); bestimmt das Dual-Control-Floor | | RequiredApprovals | int64 | die Anzahl der benötigten verschiedenen Approver | | PolicyRef | string | die Approval-Policy, die matchte; leer, wenn die Anfrage caller-supplied Parameter verwendete | | ExpiresAt | timestamp | wann die ausstehende Anfrage verfällt (fehlend = läuft nie ab) | | EscalateAt | timestamp | wann eine unentschiedene Anfrage eskaliert (fehlend = nie) |

Ein Nutzungs-/Produktivitätsmaß. Das Subject kann eine Entwicklerreferenz sein (interne E-Mail oder Key-Name), daher ist der Empfang an die privilegierte Drill-down- Permission und nicht an das Viewer-Tier-Aggregat gebunden.

| Feld | Typ | Hinweise | |---|---|---| | Name | string | natürlicher Name der Metrik, z. B. claude_code.lines_of_code.count | | Value | int64 | Maß in seiner natürlichen Integer-Einheit; Integer hält Maß-/Geld-Arithmetik exakt | | Additive | bool | true = Delta für SUM; false = Level/Snapshot, von dem das neueste bleibt | | Unit | string | lines | commits | sessions | tokens | ms | 1 | …; leer = dimensionslos | | SubjectKind, SubjectRef | string | worüber das Maß ist: developer | team | session | account | org | agent samt Referenz | | OccurredAt | timestamp | Zeitpunkt des Datenpunkts bzw. Bucket-Tag; auch producer-gesteuerter Idempotency-Key | | Dimensions | map | eigene Aufschlüsselungsachsen; strukturelle Labels, nie Payload/PII — sie gehören zum Natural Key | | Labels | map | Betreiber-Tags für Zuordnung; sie gehören nie zum Natural Key |

Terminales Gegenstück zu approval.requested mit derselben Minimal-Data-Haltung: nur Identifikatoren und Entscheidungsparameter, niemals Subject-Referenz oder Freitextgrund.

| Feld | Typ | Hinweise | |---|---|---| | ApprovalID | string | ID der entschiedenen Approval — Referenz zum Holen des vollständigen Records | | Action | string | angeforderte Action (begrenzter Kurz-Identifikator) | | SubjectKind | string | Art des Ziel-Subjects | | RiskTier | string | live abgeleitete Risikoklassifizierung zum Entscheidungszeitpunkt | | Outcome | string | approved | rejected | canceled | expiredoffen auf dem Wire; unbekannte Werte tolerieren | | RequiredApprovals | int64 | bei der Entscheidung erforderliche verschiedene Approver | | ApproveCount, RejectCount | int64 | aufgezeichnete Entscheidungen jeder Art | | PolicyRef | string | matchende Approval-Policy; leer bei caller-supplied Parametern | | DecidedAt | timestamp | Zeitpunkt des terminalen Ergebnisses |

Vom Orchestration-Modul publiziert, wenn ein governed DAG-Workflow einen eventing-emit-Schritt erreicht. Der Typ wird vom Modul festgelegt, niemals aus der Step-Konfiguration gelesen; ein Workflow-Autor kann daher kein First-Party-Event in das Ingest eines anderen Moduls fälschen. Die Konfiguration liefert nur das begrenzte Label.

| Feld | Typ | Hinweise | |---|---|---| | WorkflowRef | string | Workflow, dessen Run das Signal emittierte | | RunRef | string | Run; zusammen mit StepRef lokalisiert er den Moment in der Timeline | | StepRef | string | Referenz des emittierenden Steps im Graphen | | Label | string | vom Operator geliefertes, begrenztes, nicht-sensibles Label |

Die acht K1/K2-Work-Channels exponieren dieselbe begrenzte payload_json-Projektion eines append-only WorkEvent. Der Typ trägt die semantische Klasse; das Payload benennt Command, Result und resultierenden Aggregatzustand. Es enthält niemals Brief-Text, Acceptance-/Decision-Aussagen, Begründungen oder Owner-Freitext. Holder-Felder sind stabile Identity-Referenzen, keine Anzeigenamen. Während beta müssen Consumer additive Felder tolerieren.

| Feld | Typ | Hinweise | |---|---|---| | command | string | geschlossener Name der Durable-Work-Mutation | | result_kind | string | Art der zurückgegebenen Entität | | result_id | UUID | Referenz der zurückgegebenen Entität | | workspace_id | UUID | governed Workspace-Referenz | | work_item_id | UUID | WorkItem-Referenz | | status | string | resultierender WorkItem-Zustand | | owner_epoch | int64 | resultierende monotone Ownership-Epoch | | event_seq | int64 | resultierende monotone Sequenz im WorkItem | | lease_id | UUID | nur K2-Lease-Event: stabile WorkLease-Zeilenreferenz | | lease_state | string | nur K2-Lease-Event: resultierender Lease-Zustand | | holder_sid | string | nur K2-Lease-Event: kanonische Holder-SID | | holder_run_ref, holder_agent_ref | string | nur K2: begrenzte Run-/Agent-Referenzen, falls vorhanden | | fence | int64 | nur K2: resultierende monotone Fencing-Generation | | expires_at | timestamp | nur K2: Ablaufzeit nach DB-Uhr, solange aktiv | | end_reason_code | string | nur beendetes K2-Event: serverdefinierte Reason-Klasse, nie Operator-Freitext | | end_reason_hash | string | nur beendetes K2-Event: SHA-256-Hex des gespeicherten Grunds | | forced | boolean | nur Force-Takeover: immer true, markiert Override der Live-Lease-Autorität | | severity | string | nur Force-Takeover: fest high | | decision_id | UUID | nur Force-Takeover: effektive Governance-Decision, die den Override autorisierte | | takeover_reason_hash | string | nur Force-Takeover: SHA-256-Hex; Operator-Grund wird nie publiziert |

Die vier Force-Takeover-Felder sind eine optionale Projektion auf work.lease.acquired; normales Acquire, Renew und nicht erzwungenes Takeover tragen sie nicht. Den geschützten Operator-Grund kann ein Consumer nur autorisiert über Decision-/Audit-Referenzen holen, nicht aus diesem Event rekonstruieren.

DirectNoticeAvailableV1work.message.available

Abschnitt betitelt „DirectNoticeAvailableV1 — work.message.available“

Der unveränderliche v1-Fakt, den die DirectNotice-Source-Transaktion in WorkEvent und WorkOutbox schreibt. Die Message ist das Subject: message_id und result_id sind dieselbe UUID; result_kind ist fest sessions.message. Empfängeridentität und Inhalt fehlen bewusst; ein autorisierter Consumer folgt der Message-Referenz.

| Feld | Typ | Hinweise | |---|---|---| | schema_version | int64 | fest 1; ein späterer Writer fügt ein Schema hinzu, statt v1-Fakten umzubenennen | | command | string | fest message.publish.direct | | result_kind | string | fest sessions.message | | result_id, message_id | UUID | identische Message-Subject-Referenzen | | channel_id | UUID | governed Channel-Referenz | | message_kind | string | fest notice für diese erste K3-Write-Vertikale | | state | string | bei Verfügbarkeit fest published | | version | int64 | 2, nach atomarem draft→published-Übergang | | event_sequence | int64 | 1, erstes append-only Event des Message-Aggregats | | delivery_count | int64 | 1, da DirectNotice eine initiale Delivery auflöst | | required_count, ack_quorum | int64 | initiale Ack-Anforderung, jeweils 0 oder 1 | | fulfillment | object | initiale not_required- oder pending-Projektion samt Counts | | audience_hash | SHA-256 hex | bindet den Audience-Graphen, ohne Empfänger offenzulegen | | payload_digest | SHA-256 hex | bindet kanonischen Message-Inhalt, ohne ihn zu tragen | | plan_hash | SHA-256 hex | bindet den autorisierten Publish-Plan |

Die WorkOutbox liefert die Envelope-ID; Retries nach unklarer Abwicklung verwenden dieselbe ID und byte-identisches Payload, sodass Eventing sie als ein Event behandelt. Abweichender Typ, Source, Zeitpunkt, Payload oder Message-Subject unter derselben ID wird abgelehnt. Webhook-Consumer deduplizieren auf X-Olivares-Event; ein Operator-Replay erzeugt ein neues X-Olivares-Delivery, behält aber die Event-ID.

Die übrigen Kommunikations-WorkEvents verwenden geschlossene Minimal-Data-Projektionen. Sie tragen IDs, Zustände, monotone Versionen/Fences und Digests, niemals Message- Inhalt, DecisionResponse-Inhalt oder Handoff-Payload/-Grund. Vollständige Feldgrenzen stehen in den AsyncAPI-Schemas.

| Schema | Channels | Subject-Felder | |---|---|---| | DirectNoticeAcknowledgedV1 | work.message.acknowledged | Ack-, Delivery- und Message-IDs; Version/Zustand; Late-Flag; Fulfillment; Plan-Hash | | WorkflowMessageCarrierV1 | Workflow-Form von work.message.available; work.handoff.carrier.available; work.protocol.reply.available; work.protocol.message.received | WorkItem-, Message-, Delivery-IDs; Kind/State/Version; Sequenz; Plan-Hash; kein Remote-Inhalt | | MessageLifecycleV1 | work.message.retracted, work.message.expired, work.message.overdue | Message-/optionale WorkItem-/Aggregate-IDs; State/Version; Delivery-Count; Fulfillment; Plan-Hash | | MessageDerivedV1 | work.message.rerouted, work.message.escalated | Quell-/neue Message-ID, begrenzte Empfängerreferenz, Automationstiefe, Plan-Hash | | DecisionRequestEvent | work.decision.request.responded, work.decision.request.expired | Request-/Response-/WorkItem-IDs, Transition/State, Response-Digest | | HandoffEventV1 | work.handoff.offered, work.handoff.accepted, work.handoff.rejected, work.handoff.withdrawn, work.handoff.expired | Handoff-/Message-/Delivery-/WorkItem-IDs, State, Owner-Epoch, Lease-Fence, Plan-Hash |

ProtocolBinding schreibt eine begrenzte Projektion in den WorkItem-Eventstream, wenn es eine aus-/eingehende Bindung reserviert, eine Observation reconciliert oder eine Abbruchabsicht beansprucht. Vier Channels teilen dasselbe Schema; der Typ bestimmt die Mutation. Empfang ist durch sessions:work:read geschützt.

| Feld | Typ | Hinweise | |---|---|---| | binding_id | UUID | durable ProtocolBinding-Referenz | | binding_spec_id | UUID | immutable, von der Bindung gepinnte ProtocolBindingSpec | | binding_spec_generation | int64 | gepinnte Spec-Generation, mindestens 1 | | binding_generation | int64 | genaue repräsentierte Generation der externen Ressource | | protocol | enum | a2a | mcp | | workspace_id, work_item_id | UUID | governed Workspace-/WorkItem-Referenzen | | work_status | enum | resultierender active | review | blocked | canceled-Zustand | | lease_fence | int64 | Fencing-Generation der synthetischen Session | | verdict | enum | CLEAN | BROKEN | UNKNOWN; UNKNOWN gilt niemals als Erfolg | | code | string | begrenzter System-Reason-Code (maximal 128 Byte), nie Freitext | | terminal | boolean | ob die Observation ein terminales Remote-Ergebnis beweist | | event_seq | int64 | monotone Sequenz im WorkItem-Aggregat | | external_id_hash | SHA-256 hex | optionale Einwegkorrelation einer Remote-ID; die ID selbst fehlt |

Das Payload schließt Remote-Ressourcenreferenz/-zustand, Request-/Tool-Argumente, Task-/Message-Ergebnisse, Message-Inhalt, Observation-Detail und Operator-Abbruchgrund bewusst aus. Diese Werte bleiben hinter governed Stores; das Event exponiert nur den minimalen Reconciliation-Fakt.

Ein versiegelter Datensatz des manipulationsnachweisenden Audit-Ledgers, an ein SIEM weitergeleitet. Er läuft nicht über den Bus: Der Forwarder geht die Chain ab einem Tenant-Cursor entlang und übergibt jeden Record an Durable Intake; Integrity-Felder bleiben unverändert, sodass ein Control Tower die Chain selbst verifizieren kann.

| Feld | Typ | Hinweise | |---|---|---| | EventID | string | stabile Idempotency-ID, auf der Consumer deduplizieren | | Seq | int64 | Tenant-Ledger-Sequenz und Natural Key; Lücken sind erkennbar | | OccurredAt | timestamp | Zeitpunkt der auditierten Action | | Source | string | emittierende Komponente | | Payload | bytes | bereits encodierter Minimal-Data-Record mit Chain-Feldern (Sequenz, Previous Hash, Hash, Signatur) |

Spiegelt, was der Audit-Record der Policy-Mutation behält — Kind und Enabled — plus die ID und die Operation. Es trägt niemals den vom Operator gelieferten Policy-Namen oder die Policy-Spec; ein für governance:policy:read autorisierter Consumer holt die Policy über PolicyID.

| Feld | Typ | Hinweise | |---|---|---| | PolicyID | string | die ID der geänderten Policy | | Kind | enum | abac | approval | | Op | enum | created | updated | deleted — offen auf dem Wire; toleriere Werte, die du nicht kennst | | Enabled | bool | das Enabled-Flag nach der Änderung; false bei einer Löschung |

Das Eventing-Modul leitet die katalogisierten Event-Typen an externe HTTPS- Endpoints als signierte Webhooks weiter. Subscriptions werden unter /v1/m/eventing/subscriptions verwaltet — eine Modul-Route, bewusst außerhalb des REST-OpenAPI-Contracts — während die Event-Typen, die es ausliefert, die Per-Typ-Stabilitätsstufen oben tragen.

Jede Delivery ist ein HTTPS-POST, dessen Body der JSON-Envelope plus Seq ist — dieselben Feldnamen wie der Envelope oben (das SDK ist der Contract), mit einem additiven Feld: Seq, der Per-Tenant-Cursor, von dem ein Replay startet. Der typed Payload reitet unter Payload.

{
"ID": "0197a2b4-6e1d-7c3a-9f4e-2d8b5c1a0e7f",
"Type": "cost.sampled",
"Tenant": "",
"Source": "",
"Time": "2026-06-11T12:00:00Z",
"Seq": 42,
"Payload": { "ProviderRef": "" }
}

| Header | Bedeutung | |---|---| | X-Olivares-Timestamp | der Unix-Sekunden-Timestamp, den die Signatur abdeckt | | X-Olivares-Signature | t=<ts>,v1=<hexsig> — HMAC-SHA256 über <ts>.<body> mit dem Subscription-Secret; verifiziere mit connectors/webhook.VerifyWithin | | X-Olivares-Event | die stabile Event-ID — dein Idempotenz-Key, identisch über jeden Retry und Replay eines Events | | X-Olivares-Event-Type | der Event-Typ (die Channel-Adresse) | | X-Olivares-Delivery | die Delivery-ID — ein Replay ist eine neue Delivery des gleichen Events |

  • At-least-once. Dasselbe Event kann mehr als einmal ankommen; dedupliziere über X-Olivares-Event.
  • Retries. Ein 408, 425, 429, jedes 5xx oder ein Netzwerkfehler wird auf einer exponentiellen Backoff-Leiter wiederholt — 30s, 2m, 10m, 30m, 1h, 2h, 4h, 8h (±20% Jitter) — dann dead-lettert die Delivery (die DLQ). Jede andere Non-2xx- Response ist terminal.
  • Replay. Eine Subscription kann ab einem Seq-Cursor wiederholt werden; ein Replay behält die X-Olivares-Event-ID des Events unter einer frischen X-Olivares-Delivery.
  • Redirects werden niemals gefolgt — ein Redirect würde den signierten Body umleiten.
  • Secret-Rotation ist sofortig. Die Plattform hält genau ein Signing- Secret pro Subscription: nach POST …/rotate-secret signiert jeder spätere Versuch — einschließlich Retries bereits gequeuter Deliveries — mit dem neuen Secret. Aktualisiere zuerst deinen Verifier, dann rotiere.
  • Das Verengen von event_types ist kein Recall. Bereits für die Subscription erfasste Deliveries bleiben gequeut und werden ausgeliefert (die Per-Typ-Permission gilt weiterhin); der verengte Filter bestimmt, was von da an erfasst wird.

Eine Subscription benennt eine Rolle; vor jedem Delivery-Versuch wird diese Rolle gegen die Permission des Event-Typs evaluiert, durch die volle RBAC+ABAC- Pipeline (deny-closed). Das Mapping spiegelt die Read-Oberfläche jedes Typs in der Produkt-API:

| Event-Typ | Permission | Stabilität | |---|---|---| | edge.observed | accessgraph:read (privilegiert: editor+) | stable | | cost.sampled | finops:spend:read | stable | | finding.reported | security:finding:read | stable | | guardrail.observed | security:observed:read (privilegiert: editor+) | beta | | approval.requested | governance:approval:read | beta | | policy.changed | governance:policy:read | beta | | metric.sampled | adoption:developer:read (privilegiert: editor+) | beta | | approval.resolved | governance:approval:read | beta | | workflow.signal | orchestration:workflow:read | beta | | work.item.created | sessions:work:read | beta | | work.item.transitioned | sessions:work:read | beta | | work.owner.changed | sessions:work:read | beta | | work.dependency.changed | sessions:work:read | beta | | work.acceptance.changed | sessions:work:read | beta | | work.message.available | sessions:message:read | beta | | work.message.acknowledged | sessions:message:read | beta | | work.message.retracted | sessions:message:read | beta | | work.message.expired | sessions:message:read | beta | | work.message.overdue | sessions:message:read | beta | | work.message.rerouted | sessions:message:read | beta | | work.message.escalated | sessions:message:read | beta | | work.protocol.reply.available | sessions:message:read | beta | | work.protocol.message.received | sessions:message:read | beta | | work.handoff.carrier.available | sessions:message:read | beta | | work.decision.recorded | sessions:decision:read | beta | | work.decision.request.responded | sessions:decision-request:read | beta | | work.decision.request.expired | sessions:decision-request:read | beta | | work.handoff.offered | sessions:handoff:read | beta | | work.handoff.accepted | sessions:handoff:read | beta | | work.handoff.rejected | sessions:handoff:read | beta | | work.handoff.withdrawn | sessions:handoff:read | beta | | work.handoff.expired | sessions:handoff:read | beta | | work.lease.acquired | sessions:lease:read | beta | | work.lease.ended | sessions:lease:read | beta | | work.binding.reserved | sessions:work:read | beta | | work.binding.observed | sessions:work:read | beta | | work.binding.ambiguous | sessions:work:read | beta | | work.binding.cancel_requested | sessions:work:read | beta | | audit.recorded | audit:read | stable |