# Standards-Zuordnung / Standards Mapping

**Open Mandate Protocol (OMP) ↔ Verifiable Intent (VI) ↔ AP2 ↔ Web Bot Auth**

Stand / As of: 2026-09-20 · VI-Referenz: `agent-intent/verifiable-intent`, Spez. 0.1-draft (2026-02-18), Apache 2.0

---

## Deutsch

### Worum es geht

Die Frage «Folgt Mandact den entstehenden Agenten-Standards?» hat eine kurze
und eine genaue Antwort. Die kurze: **Wir bauen auf denselben Primitiven** —
SD-JWT-Credentials, selektive Offenlegung, schlüsselgebundene Delegation vom
Menschen zum Agenten, maschinenlesbare Grenzen. Die genaue steht in diesem
Dokument: Feld für Feld, mit den echten Claim-Namen beider Seiten, inklusive
dem, was NICHT deckungsgleich ist.

Die Landschaft, auf die sich die Frage bezieht:

| Initiative | Träger | Gegenstand | Verhältnis zu OMP |
| --- | --- | --- | --- |
| **Verifiable Intent (VI)** | Mastercard / `agent-intent` (Apache 2.0) | Dreischichtige SD-JWT-Kette für den agentischen **Kauf**: Principal → Mandat → Transaktionskontext | Gleiche Primitive; Brücke implementiert (`lib/interop/vi.ts`), gegen die Referenzimplementierung geprüft (`npm run vi:referenz`) |
| **AP2** | Google (Agent Payments Protocol) | Mandate für Agenten-Zahlungen (Intent/Cart/Payment Mandates); daneben trägt das Repo die konvergierte AP2/VI-dSD-JWT-Kette | Annahme des klassischen Trios implementiert (`lib/interop/ap2.ts`, `POST /v1/interop/ap2`): Struktur exakt nach Googles pydantic-Modellen, Händler-Signatur samt `cart_hash`-Bindung, Entscheid durch dieselbe Engine; geprüft via `npm run ap2:annahme` gegen die vendorte Referenz (`tests/fixtures/ap2/referenz/`). Die konvergierte Kette teilt die VI-Primitive und deckt die VI-Brücke |
| **FIDO Agentic-Standards** (Agentic Authentication TWG + Payments TWG, gegründet 28.04.2026) | FIDO Alliance | Authentisierung von Agenten; die **Payments TWG** entwickelt die Agentic-Commerce-Spezifikation auf Basis der Beiträge **Google AP2** und **Mastercard Verifiable Intent** — beide hier gegen die Referenz implementiert | Beteiligung angestrebt (M9, Weg: Associate-Mitgliedschaft — Mitarbeit in Working Groups möglich, ohne Chair-Rolle); Beitrag zu VI §9.2 `agent_attestation` vorbereitet (`docs/VI-CONTRIBUTION-AGENT-ATTESTATION.md`) |
| **Visa TAP / Web Bot Auth** | Visa / IETF (RFC 9421) | Agent erkennt sich auf **Transportebene** (HTTP Message Signatures) | Dokumentiert in `docs/WEB-BOT-AUTH.md`; Adapter in Vorbereitung (Sprint S7). Transport ergänzt Autorisierung, ersetzt sie nicht |

### Die Schichten

VI und OMP schneiden das Problem gleich — die Namen unterscheiden sich:

| VI | OMP | Anmerkung |
| --- | --- | --- |
| **L1 · Principal Identity Credential** (`sd+jwt`, ES256, Issuer stellt aus, `cnf.jwk` = Schlüssel des Menschen) | **Principal-Anker** (Identität des Mandatsgebers, `verification_level`, E-ID-Andockpunkt) | VI lässt den Issuer offen (§3.3, eigene `vct`-Profile ausdrücklich erlaubt). Genau hier gehört ein **qualifizierter Vertrauensdienst** hin — die Schicht, die VI bewusst nicht standardisiert |
| **L2 · Mandate** (`kb-sd-jwt+kb`, signiert vom User-Schlüssel aus L1 — «or an authorized system holding the user's private key»; max. 30 Tage) | **Mandat** `omp/mandate/1` (SD-JWT VC, Custody-Modell) | Der zitierte VI-Halbsatz IST das Mandact-Custody-Modell. Autonomous = Grenzen vorab (OMP-Normalfall); Immediate = Mensch bestätigt Endwerte (OMP: Step-up/Eskalation, M4) |
| **L3 · Transaction Context** (`kb-sd-jwt`, Agent-Schlüssel, ES256) | **Presentation + `/v1/verify` + Receipt** (Intent-Hash, signierter Beleg) | Gleiche Rolle — die konkrete Transaktion unter dem Mandat, vom Agenten signiert. Nicht formatidentisch; die Brücke deckt heute L1+L2 |

### Feld für Feld: Grenzen

Die Schlüssel links sind exakt die aus `mandates.constraints` und dem
Credential `omp/mandate/1`; rechts die VI-Constraint-Typen aus der
Spezifikation 0.1-draft.

| OMP | VI | AP2 (sinngemäss) | Deckung |
| --- | --- | --- | --- |
| `amount_limit_per_action` + `currency` | `mandate.payment.amount_range` (`{currency, min?, max}`, **Minor Units** nach ISO 4217) | Payment-Mandate-Betrag | Verlustfrei; die Brücke rechnet Haupt- in Minor-Einheiten um (Exponententabelle, CHF → Rappen) |
| `amount_limit_per_month` | `mandate.payment.budget` (**kumulativ je Mandatspaar**, nicht rollierend) | Budget im Intent Mandate | **Nicht identisch.** OMP: rollierendes Monatsfenster. VI: Summe über die Mandatslaufzeit. Die Brücke exportiert nur bei Laufzeit ≤ 31 Tagen — sonst ehrlich `unmapped` mit Grund |
| `counterparty_allow` | `mandate.checkout.allowed_merchants` (`website` **Pflicht**, WYSIWYS; leere Liste = unerfüllbar) / `mandate.payment.allowed_payees` | Merchant-Bindung im Cart Mandate | OMP führt Namen, VI verlangt die Website. Die Brücke exportiert eine Gegenpartei nur mit Website-Eintrag im Verzeichnis — sonst `unmapped` |
| `counterparty_deny` | — | — | VI kennt keine Negativliste. Bleibt OMP-seitig durchgesetzt (MD-304); Export meldet `unmapped` |
| `line_items_allow` (M6, MD-312) | `mandate.checkout.line_items` (`{items:[{id, acceptable_items, quantity}]}`) | Cart-Positionen | Verlustfrei — die Positionen-Positivliste wurde für diese Parität ins OMP-Vokabular aufgenommen (fail-closed: Weglassen ist keine Umgehung) |
| `frequency_limit` (MD-305) | `mandate.payment.agent_recurrence` (`frequency` ISO-20022-artig: `ON_DEMAND`/`DAIL`/`MNTH`…, `start_date`, `end_date`, `max_occurrences?`; **verlangt** `amount_range` + `budget` im selben Mandat) | Recurrence im Intent Mandate | Teilweise: `max_occurrences` ist eine Gesamtzahl, OMP zählt je Fenster. Verlustfrei nur bei Laufzeit ≤ 1 Fenster — sonst `unmapped` mit Grund |
| `escalation.threshold` + Step-up (M4, MD-401) | **Immediate Mode** (Mensch bestätigt die Endwerte) | Human-present-Pfad | Gleiches Konzept, andere Mechanik: VI signiert die Bestätigung in die Kette, OMP macht sie zum atomaren Freigabe-Token (`Mandact-Escalation-Token`) mit Beweis |
| `single_use` | Bindung Checkout↔Payment via `conditional_transaction_id` (Disclosure-Hash) | einmaliges Cart Mandate | Verwandt: beide binden die Ausführung an genau einen Vorgang |
| `resource_scope`, `quantity_limit`, `duration_limit_days`, `recipient_limit`, `geo`, `time_window` | — | — | **OMP-Mehrwert.** VI ist ein Kauf-Format; diese Grenzen tragen die 116 Handlungstypen jenseits des Kaufs |
| Agent-Bindung: `cnf` (Ed25519 **oder** ES256) | L2-Disclosure `cnf.jwk` mit `kid` (**ES256 P-256 Pflicht**) | Agent-Key im Mandate | Die Brücke verlangt einen ES256-Schlüssel; ein Ed25519-Agent wird nicht still konvertiert, sondern mit Grund abgewiesen |
| `vct: omp/mandate/1` | `mandate.checkout.open.1` / `mandate.payment.open.1`; eigene Profile per §3.3 | AP2-Mandate-Typen | Die Brücke nutzt das Mandact-Profil `https://mandact.com/vct/vi/principal/1` für L1 — spezifikationskonform, prüfbar via `expected_l1_vct` der Referenz |

### Was OMP trägt und VI bewusst ausklammert

VI standardisiert das Credential für den Kauf. Mandact ist die Schicht, die
VI ausklammert — und die im Betrieb den Unterschied macht:

| Fähigkeit | OMP | VI 0.1-draft |
| --- | --- | --- |
| Qualifizierter Identitätsanker (Vertrauensdienst, E-ID) | Andockpunkt, `verification_level` | Offen gelassen — «beliebiger Issuer» |
| Widerruf im Betrieb (Status-Endpoint, Kill-Switch) | Ja | Nur `exp` |
| Eskalation als Prozess (anlegen → Mensch entscheidet → Token einlösen, atomar, mit Beweis) | Ja (M4) | Immediate als Signatur-Modus, kein Prozess |
| Signierte Belege (Receipts) + Trace | Ja | Nein |
| Verifier-Policy (MD-5xx), Risiko-Kontext | Ja | Nein |
| Enforcement (Gateways/PEP, Execution Capability, Einmal-Verbrauch) | Ja | Ausserhalb des Scopes |
| Handlungstypen jenseits des Kaufs (123 gesamt, davon 116 nicht `purchase.*`/`payment.*`/`subscription.*`: `access.grant`, `data.share`, `hr.*`, `legal.*` …) | Ja | Kauf-Fokus |
| Rechtsraum + Formstufen (QES-Pflicht, `prepare_only`, MD-404/405) | Ja | Nein |
| Delegationstiefe / Ketten | Ja | Eine Stufe (Principal → Agent) |

Und umgekehrt, was VI hat und OMP heute nicht 1:1 führt: den
WYSIWYS-`prompt_summary` (der dem Menschen gezeigte Wortlaut als Claim), das
`payment_instrument` **im** Mandat, und L3 als eigenes Agent-signiertes
Kontext-Credential. Die Brücke meldet solche Lücken als `unmapped` mit Grund
— Ehrlichkeit ist Teil des Formats.

### Die AP2-Annahme, prüfbar

Das klassische AP2-Trio wird ANGENOMMEN und von derselben Engine entschieden
— keine zweite Wahrheit: `intent.merchants` → `counterparty_allow` (MD-304),
`intent.skus` → `line_items_allow` (MD-312), `intent.intent_expiry` →
Gültigkeitsfenster (MD-203), `user_cart_confirmation_required` → Eskalation
(MD-401) mit Freigabe über den Step-up-Token (M4). Ehrlich benannt: Das Trio
trägt kein nutzerseitiges Betragslimit (der Betrag ist Eigenschaft der
Handlung), `requires_refundability` hat kein OMP-Pendant, und AP2 0.1 lässt
die `cart_hash`-Kanonisierung offen — die Annahme legt sie fest
(RFC-8785-artig) und der Check signiert python-seitig mit Googles
`jwt_helper` über exakt diese Kanonisierung. `npm run ap2:annahme` schreibt
den Anker `tests/fixtures/ap2/ANNAHME-GRUEN.md` nur bei GRÜN (M10).

### Die Brücke, prüfbar

- `lib/interop/vi.ts` — Export eines aktiven OMP-Mandats als VI L1+L2
  (Autonomous), mit Report (`mapped` / `unmapped` / `clamped`), und die
  Gegenrichtung als Befugnis-Sicht.
- `POST /v1/interop/vi` — `direction: "export" | "map" | "accept"`
  (Preview-Brücke, `verify`-Scope). `accept` ist der M2-Import: die Kette
  vollständig verifiziert (L1 gegen den benannten Aussteller, sd_hash,
  L2 gegen L1 `cnf.jwk`, Mandatspaar), fail-closed übersetzt — und
  **dieselbe Engine entscheidet** (allow/deny/escalate mit MD-Codes). Ein
  Constraint, den OMP nicht durchsetzen kann, wird nicht angenommen.
- Der L1-Aussteller ist **injizierbar** (`ViL1Issuer`): Der qualifizierte
  Anker (M3, Swisscom Trust Services) ist ein Drop-in, kein Umbau — der
  Referenz-Check verifiziert die Kette auch mit fremdem Aussteller
  (Schnittstelle: `docs/M3-QUALIFIED-ANCHOR-SWISSCOM.md`).
- `npm run vi:referenz` — führt den Export gegen die **vendorte
  Referenzimplementierung** (`tests/fixtures/vi/referenz/`, Apache 2.0,
  Herkunft dokumentiert): `verify_chain` und `check_constraints` müssen die
  Mandact-Kette annehmen und die Verletzung ablehnen. Nur bei GRÜN entsteht
  der Anker `tests/fixtures/vi/REFERENZ-GRUEN.md` — und nur mit diesem Anker
  erlaubt das Claims-Register (M10) eine Kompatibilitäts-Formulierung.

---

## English

### What this is

The short answer to "does Mandact follow the emerging agent standards" is:
**we build on the same primitives** — SD-JWT credentials, selective
disclosure, key-bound delegation from human to agent, machine-readable
limits. The precise answer is this document: field by field, using both
sides' real claim names, including what is *not* congruent.

The landscape:

| Initiative | Steward | Subject | Relation to OMP |
| --- | --- | --- | --- |
| **Verifiable Intent (VI)** | Mastercard / `agent-intent` (Apache 2.0) | Three-layer SD-JWT chain for agentic **purchases**: principal → mandate → transaction context | Same primitives; bridge implemented (`lib/interop/vi.ts`), checked against the reference implementation (`npm run vi:referenz`) |
| **AP2** | Google (Agent Payments Protocol) | Mandates for agent payments (intent/cart/payment mandates); the repo also carries the converged AP2/VI dSD-JWT chain | Acceptance of the classic trio implemented (`lib/interop/ap2.ts`, `POST /v1/interop/ap2`): structure exactly per Google's pydantic models, merchant signature incl. `cart_hash` binding, decided by the same engine; checked via `npm run ap2:annahme` against the vendored reference (`tests/fixtures/ap2/referenz/`). The converged chain shares VI's primitives and is covered by the VI bridge |
| **FIDO agentic standards** (Agentic Authentication TWG + Payments TWG, formed 2026-04-28) | FIDO Alliance | Agent authentication; the **Payments TWG** develops the agentic-commerce specification from the **Google AP2** and **Mastercard Verifiable Intent** contributions — both implemented here against the reference | Participation sought (M9, route: associate membership — working-group participation possible, no chair role); contribution to VI §9.2 `agent_attestation` prepared (`docs/VI-CONTRIBUTION-AGENT-ATTESTATION.md`) |
| **Visa TAP / Web Bot Auth** | Visa / IETF (RFC 9421) | Agent identifies itself at the **transport layer** (HTTP message signatures) | Documented in `docs/WEB-BOT-AUTH.md`; adapter in preparation (sprint S7). Transport complements authorization; it does not replace it |

### The layers

| VI | OMP | Note |
| --- | --- | --- |
| **L1 · Principal Identity Credential** (`sd+jwt`, ES256, issued by an issuer, `cnf.jwk` = the human's key) | **Principal anchor** (mandator identity, `verification_level`, E-ID attachment point) | VI leaves the issuer open (§3.3 explicitly permits own `vct` profiles). This is exactly where a **qualified trust service** belongs — the layer VI deliberately does not standardize |
| **L2 · Mandate** (`kb-sd-jwt+kb`, signed by the user key from L1 — "or an authorized system holding the user's private key"; max. 30 days) | **Mandate** `omp/mandate/1` (SD-JWT VC, custody model) | That quoted VI clause *is* the Mandact custody model. Autonomous = limits up front (OMP default); Immediate = human confirms final values (OMP: step-up/escalation, M4) |
| **L3 · Transaction Context** (`kb-sd-jwt`, agent key, ES256) | **Presentation + `/v1/verify` + receipt** (intent hash, signed evidence) | Same role — the concrete transaction under the mandate, signed by the agent. Not format-identical; the bridge covers L1+L2 today |

### Field by field: limits

Left-hand keys are exactly those of `mandates.constraints` and the
`omp/mandate/1` credential; right-hand names are the VI constraint types of
spec 0.1-draft.

| OMP | VI | AP2 (in spirit) | Coverage |
| --- | --- | --- | --- |
| `amount_limit_per_action` + `currency` | `mandate.payment.amount_range` (`{currency, min?, max}`, **minor units** per ISO 4217) | payment-mandate amount | Lossless; the bridge converts major to minor units (exponent table, CHF → Rappen) |
| `amount_limit_per_month` | `mandate.payment.budget` (**cumulative per mandate pair**, not rolling) | budget in the intent mandate | **Not identical.** OMP: rolling monthly window. VI: sum over the mandate's lifetime. The bridge exports only for validity ≤ 31 days — otherwise honestly `unmapped`, with reason |
| `counterparty_allow` | `mandate.checkout.allowed_merchants` (`website` **required**, WYSIWYS; empty list = unsatisfiable) / `mandate.payment.allowed_payees` | merchant binding in the cart mandate | OMP carries names, VI requires the website. The bridge exports a counterparty only with a website entry in the directory — otherwise `unmapped` |
| `counterparty_deny` | — | — | VI has no deny list. Enforced on the OMP side (MD-304); export reports `unmapped` |
| `line_items_allow` (M6, MD-312) | `mandate.checkout.line_items` (`{items:[{id, acceptable_items, quantity}]}`) | cart line items | Lossless — the line-item allow list was added to the OMP vocabulary for this parity (fail-closed: omission is not a bypass) |
| `frequency_limit` (MD-305) | `mandate.payment.agent_recurrence` (`frequency` ISO-20022-style: `ON_DEMAND`/`DAIL`/`MNTH`…, `start_date`, `end_date`, `max_occurrences?`; **requires** `amount_range` + `budget` in the same mandate) | recurrence in the intent mandate | Partial: `max_occurrences` is a total, OMP counts per window. Lossless only for validity ≤ 1 window — otherwise `unmapped`, with reason |
| `escalation.threshold` + step-up (M4, MD-401) | **Immediate mode** (human confirms final values) | human-present path | Same concept, different mechanics: VI signs the confirmation into the chain, OMP turns it into an atomic approval token (`Mandact-Escalation-Token`) with evidence |
| `single_use` | checkout↔payment binding via `conditional_transaction_id` (disclosure hash) | one-off cart mandate | Related: both bind execution to exactly one transaction |
| `resource_scope`, `quantity_limit`, `duration_limit_days`, `recipient_limit`, `geo`, `time_window` | — | — | **OMP surplus.** VI is a purchase format; these limits carry the 116 action types beyond purchasing |
| Agent binding: `cnf` (Ed25519 **or** ES256) | L2 disclosure `cnf.jwk` with `kid` (**ES256 P-256 required**) | agent key in the mandate | The bridge requires an ES256 key; an Ed25519 agent is not silently converted but rejected with a reason |
| `vct: omp/mandate/1` | `mandate.checkout.open.1` / `mandate.payment.open.1`; own profiles per §3.3 | AP2 mandate types | The bridge uses the Mandact profile `https://mandact.com/vct/vi/principal/1` for L1 — spec-conformant, checkable via the reference's `expected_l1_vct` |

### What OMP carries that VI deliberately leaves out

VI standardizes the credential for the purchase. Mandact is the layer VI
leaves out — the one that makes the difference in operation:

| Capability | OMP | VI 0.1-draft |
| --- | --- | --- |
| Qualified identity anchor (trust service, E-ID) | attachment point, `verification_level` | left open — "any issuer" |
| Runtime revocation (status endpoint, kill switch) | yes | `exp` only |
| Escalation as a process (create → human decides → token redeemed, atomic, with evidence) | yes (M4) | Immediate as a signing mode, not a process |
| Signed receipts + trace | yes | no |
| Verifier policy (MD-5xx), risk context | yes | no |
| Enforcement (gateways/PEP, execution capability, single consumption) | yes | out of scope |
| Action types beyond purchasing (123 total, 116 outside `purchase.*`/`payment.*`/`subscription.*`: `access.grant`, `data.share`, `hr.*`, `legal.*` …) | yes | purchase focus |
| Jurisdiction + form requirements (QES, `prepare_only`, MD-404/405) | yes | no |
| Delegation depth / chains | yes | one hop (principal → agent) |

Conversely, VI carries what OMP does not hold 1:1 today: the WYSIWYS
`prompt_summary` (the wording shown to the human, as a claim), the
`payment_instrument` **inside** the mandate, and L3 as a separate
agent-signed context credential. The bridge reports such gaps as `unmapped`
with a reason — honesty is part of the format.

### AP2 acceptance, verifiable

The classic AP2 trio is ACCEPTED and decided by the same engine — no second
truth: `intent.merchants` → `counterparty_allow` (MD-304), `intent.skus` →
`line_items_allow` (MD-312), `intent.intent_expiry` → validity window
(MD-203), `user_cart_confirmation_required` → escalation (MD-401) released
via the step-up token (M4). Named honestly: the trio carries no user-side
amount limit (the amount is a property of the action),
`requires_refundability` has no OMP counterpart, and AP2 0.1 leaves the
`cart_hash` canonicalization open — acceptance pins it (RFC-8785-style) and
the check signs on the Python side with Google's `jwt_helper` over exactly
that canonicalization. `npm run ap2:annahme` writes the anchor
`tests/fixtures/ap2/ANNAHME-GRUEN.md` only on GREEN (M10).

### The bridge, verifiable

- `lib/interop/vi.ts` — exports an active OMP mandate as VI L1+L2
  (Autonomous) with a report (`mapped` / `unmapped` / `clamped`), plus the
  reverse direction as an authority view.
- `POST /v1/interop/vi` — `direction: "export" | "map" | "accept"`
  (preview bridge, `verify` scope). `accept` is the M2 import: the chain
  fully verified (L1 against the named issuer, sd_hash, L2 against L1
  `cnf.jwk`, mandate pair), translated fail-closed — and **the same engine
  decides** (allow/deny/escalate with MD codes). A constraint OMP cannot
  enforce is not accepted.
- The L1 issuer is **injectable** (`ViL1Issuer`): the qualified anchor (M3,
  Swisscom Trust Services) is a drop-in, not a rebuild — the reference check
  verifies the chain with a foreign issuer as well (interface:
  `docs/M3-QUALIFIED-ANCHOR-SWISSCOM.md`).
- `npm run vi:referenz` — runs the export against the **vendored reference
  implementation** (`tests/fixtures/vi/referenz/`, Apache 2.0, provenance
  documented): `verify_chain` and `check_constraints` must accept the
  Mandact chain and reject the violation. Only on GREEN does the anchor
  `tests/fixtures/vi/REFERENZ-GRUEN.md` come into existence — and only with
  that anchor does the claims registry (M10) permit any compatibility
  wording.
