Skip to content

Commit 94d1aaa

Browse files
naoryNAOR YUVALclaude
authored
feat(PR24): Automated Fleet Trip Demo (#40)
Adds examples/fleet-trip/ — a robotaxi completes a full commercial trip across three service types (toll, EV charging, parking) with cumulative budget enforcement and a post-trip fleet audit. Key behaviors demonstrated: - Multi-payment session: same SignedPolicyGrant and SBA used across 3 stops - Cumulative budget enforcement: wallet tracks spend; 4th payment ($44 > $40) refused - Signed PolicyGrant: fleet operator signs grant; verifier enforces signature - destinationAllowlist: only pre-approved service types accepted - Stateless audit: each bundle verifies independently after trip ends - Tamper detection: modified charging bundle detected at audit time New files: - examples/fleet-trip/demo-fleet-trip.mjs — runnable narrative demo - examples/fleet-trip/fleet-policy.json — fleet operator policy definition - examples/fleet-trip/README.md — setup, scenario table, enforcement model - examples/fleet-trip/bundle-stop*.json — generated settlement bundles New script: npm run example:fleet-trip Co-authored-by: NAOR YUVAL <naoryuval@NAORs-MacBook-Air.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 6dec6d2 commit 94d1aaa

8 files changed

Lines changed: 1064 additions & 1 deletion

examples/fleet-trip/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Fleet Trip Demo
2+
3+
Demonstrates a robotaxi (EV-001) completing a full commercial trip with three autonomous service payments, cumulative budget enforcement, and a post-trip fleet audit.
4+
5+
## What It Shows
6+
7+
| Concept | Description |
8+
|---------|-------------|
9+
| Multi-payment session | Same PolicyGrant and SBA used across 3 payments |
10+
| Cumulative budget enforcement | Vehicle wallet tracks spend; 4th payment refused |
11+
| Signed PolicyGrant | Fleet operator signs grant; verifier enforces signature |
12+
| Destination allowlist | Only pre-approved service types accepted |
13+
| Stateless audit | Each bundle verifies independently after the trip |
14+
| Tamper detection | Modified settlement amount detected at audit time |
15+
16+
## Trip Scenario
17+
18+
**Vehicle:** EV-001 (robotaxi, fleet-robotaxi-west)
19+
**Session budget:** $40.00 XRPL / RLUSD
20+
**Allowed destinations:** toll booth, EV charging, parking garage
21+
22+
| Stop | Service | Amount | Cumulative |
23+
|------|---------|--------|------------|
24+
| 1 | Toll Booth (I-280 N) | $6.00 | $6.00 |
25+
| 2 | EV Charging (ChargePoint SF-04) | $18.00 | $24.00 |
26+
| 3 | Parking Garage (Market St) | $12.00 | $36.00 |
27+
| 4 | Charging again | $8.00 | **REJECTED** ($44 > $40 budget) |
28+
29+
## Run
30+
31+
```bash
32+
npm run build
33+
npm run example:fleet-trip
34+
```
35+
36+
## Generated Files
37+
38+
| File | Description |
39+
|------|-------------|
40+
| `bundle-stop1-toll.json` | Settlement bundle — toll payment |
41+
| `bundle-stop2-charging.json` | Settlement bundle — EV charging payment |
42+
| `bundle-stop3-parking.json` | Settlement bundle — parking payment |
43+
| `bundle-stop2-charging-TAMPERED.json` | Modified bundle (tamper detection demo) |
44+
45+
Each bundle is self-contained and can be verified independently:
46+
47+
```bash
48+
npx mpcp verify examples/fleet-trip/bundle-stop1-toll.json --explain
49+
npx mpcp verify examples/fleet-trip/bundle-stop3-parking.json --explain
50+
npx mpcp verify examples/fleet-trip/bundle-stop2-charging-TAMPERED.json --explain
51+
# Last one fails: SignedPaymentAuthorization.valid payment_auth_mismatch
52+
```
53+
54+
## How Cumulative Budget Enforcement Works
55+
56+
The vehicle wallet acts as the **session authority** — it tracks cumulative spending and refuses to sign a new SPA when the running total would exceed `maxAmountMinor`.
57+
58+
```
59+
Before Stop 1: cumulativeSpent = $0 → $0 + $6 = $6 ≤ $40 ✓
60+
Before Stop 2: cumulativeSpent = $6 → $6 + $18 = $24 ≤ $40 ✓
61+
Before Stop 3: cumulativeSpent = $24 → $24 + $12 = $36 ≤ $40 ✓
62+
Before Stop 4: cumulativeSpent = $36 → $36 + $8 = $44 > $40 ✗ REJECTED
63+
```
64+
65+
The verifier is stateless — it checks each bundle independently. Cumulative enforcement lives in the wallet, which must persist this counter in trusted local storage.
66+
67+
## See Also
68+
69+
- [Machine Wallet Guardrails](../../docs/implementation/machine-wallet-guardrails.md)
70+
- [SignedBudgetAuthorization — Cumulative Enforcement](https://mpcp-protocol.github.io/mpcp-spec/protocol/SignedBudgetAuthorization/#cumulative-enforcement)
71+
- [Offline Payment Demo](../parking/demo-offline.mjs) — same concept, no connectivity
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{
2+
"settlement": {
3+
"amount": "6000000",
4+
"rail": "xrpl",
5+
"asset": {
6+
"kind": "IOU",
7+
"currency": "RLUSD",
8+
"issuer": "rIssuer"
9+
},
10+
"destination": "rTollBooth",
11+
"nowISO": "2026-03-14T09:12:00Z"
12+
},
13+
"settlementIntent": {
14+
"version": "1.0",
15+
"rail": "xrpl",
16+
"amount": "6000000",
17+
"createdAt": "2026-03-14T09:12:00Z",
18+
"destination": "rTollBooth",
19+
"asset": {
20+
"kind": "IOU",
21+
"currency": "RLUSD",
22+
"issuer": "rIssuer"
23+
}
24+
},
25+
"spa": {
26+
"authorization": {
27+
"version": "1.0",
28+
"decisionId": "dec-toll-001",
29+
"sessionId": "trip-20260314-EV001",
30+
"policyHash": "a1b2c3d4e5f6",
31+
"budgetId": "d26d4e94-dac4-4af6-81ad-7fd4080a2779",
32+
"quoteId": "q-toll-001",
33+
"rail": "xrpl",
34+
"asset": {
35+
"kind": "IOU",
36+
"currency": "RLUSD",
37+
"issuer": "rIssuer"
38+
},
39+
"amount": "6000000",
40+
"destination": "rTollBooth",
41+
"intentHash": "cdf6b71ed131b1cdda74962727d06430644d2b4fb030cc72b6a19cc2e87c5003",
42+
"expiresAt": "2030-12-31T23:59:59Z",
43+
"nonce": "4009f172-7591-4d45-8d8d-fc109fca72a6"
44+
},
45+
"issuerKeyId": "fleet-spa-signing-key-1",
46+
"signature": "HnMSCzbrLZ0VI+GisKzwjvDdfsXcmOcha5XLXsz8lJxd5JFuB8iHP0+QvFcYbBPeGGLmPUlhVKzu8O865c8EBQ=="
47+
},
48+
"sba": {
49+
"authorization": {
50+
"version": "1.0",
51+
"budgetId": "d26d4e94-dac4-4af6-81ad-7fd4080a2779",
52+
"grantId": "84fff5d0-8656-4b2d-ab82-15fa36427c38",
53+
"sessionId": "trip-20260314-EV001",
54+
"vehicleId": "EV-001",
55+
"policyHash": "a1b2c3d4e5f6",
56+
"currency": "USD",
57+
"minorUnit": 2,
58+
"budgetScope": "SESSION",
59+
"maxAmountMinor": "4000",
60+
"allowedRails": [
61+
"xrpl"
62+
],
63+
"allowedAssets": [
64+
{
65+
"kind": "IOU",
66+
"currency": "RLUSD",
67+
"issuer": "rIssuer"
68+
}
69+
],
70+
"destinationAllowlist": [
71+
"rTollBooth",
72+
"rChargingStation",
73+
"rParkingGarage"
74+
],
75+
"expiresAt": "2030-12-31T23:59:59Z"
76+
},
77+
"issuerKeyId": "fleet-sba-signing-key-1",
78+
"signature": "U3MIfbuyy2CE7PmQB7AAsJLV3+DpBcOEokXBnrcU7m5Ud9UhEPObEtxSy6yGPY3TTspdtLqJOJEz2i7lio88Dg=="
79+
},
80+
"policyGrant": {
81+
"grantId": "84fff5d0-8656-4b2d-ab82-15fa36427c38",
82+
"policyHash": "a1b2c3d4e5f6",
83+
"expiresAt": "2030-12-31T23:59:59Z",
84+
"allowedRails": [
85+
"xrpl"
86+
],
87+
"allowedAssets": [
88+
{
89+
"kind": "IOU",
90+
"currency": "RLUSD",
91+
"issuer": "rIssuer"
92+
}
93+
],
94+
"issuer": "did:web:fleet.robotaxi-west.example.com",
95+
"issuerKeyId": "fleet-policy-grant-key-1",
96+
"signature": "LoWlfxnzxWBaG0WmFhyBnITlujxETOE7f7OsM4KQ7IDYNyERnau8dG14vFt0DcrRthBTbFL+VMQPSMulV/VgCw=="
97+
},
98+
"paymentPolicyDecision": {
99+
"decisionId": "dec-toll-001",
100+
"policyHash": "a1b2c3d4e5f6",
101+
"action": "ALLOW",
102+
"reasons": [
103+
"Within budget",
104+
"Destination authorized"
105+
],
106+
"expiresAtISO": "2030-12-31T23:59:59Z",
107+
"rail": "xrpl",
108+
"asset": {
109+
"kind": "IOU",
110+
"currency": "RLUSD",
111+
"issuer": "rIssuer"
112+
},
113+
"priceFiat": {
114+
"amountMinor": "600",
115+
"currency": "USD"
116+
},
117+
"chosen": {
118+
"rail": "xrpl",
119+
"quoteId": "q-toll-001"
120+
},
121+
"settlementQuotes": [
122+
{
123+
"quoteId": "q-toll-001",
124+
"rail": "xrpl",
125+
"amount": {
126+
"amount": "6000000",
127+
"decimals": 6
128+
},
129+
"destination": "rTollBooth",
130+
"expiresAt": "2030-12-31T23:59:59Z",
131+
"asset": {
132+
"kind": "IOU",
133+
"currency": "RLUSD",
134+
"issuer": "rIssuer"
135+
}
136+
}
137+
]
138+
},
139+
"sbaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAr7H9fidwL/o6BxBbWDp864CFpXpW4xehPl3LY3zMAw8=\n-----END PUBLIC KEY-----\n",
140+
"spaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEApNN61UHHMjipJXLvQ+gzZsGjjMznj3lGzwtJmhWitiw=\n-----END PUBLIC KEY-----\n"
141+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{
2+
"settlement": {
3+
"amount": "19500000",
4+
"rail": "xrpl",
5+
"asset": {
6+
"kind": "IOU",
7+
"currency": "RLUSD",
8+
"issuer": "rIssuer"
9+
},
10+
"destination": "rChargingStation",
11+
"nowISO": "2026-03-14T10:45:00Z"
12+
},
13+
"settlementIntent": {
14+
"version": "1.0",
15+
"rail": "xrpl",
16+
"amount": "18000000",
17+
"createdAt": "2026-03-14T10:45:00Z",
18+
"destination": "rChargingStation",
19+
"asset": {
20+
"kind": "IOU",
21+
"currency": "RLUSD",
22+
"issuer": "rIssuer"
23+
}
24+
},
25+
"spa": {
26+
"authorization": {
27+
"version": "1.0",
28+
"decisionId": "dec-charge-001",
29+
"sessionId": "trip-20260314-EV001",
30+
"policyHash": "a1b2c3d4e5f6",
31+
"budgetId": "d26d4e94-dac4-4af6-81ad-7fd4080a2779",
32+
"quoteId": "q-charge-001",
33+
"rail": "xrpl",
34+
"asset": {
35+
"kind": "IOU",
36+
"currency": "RLUSD",
37+
"issuer": "rIssuer"
38+
},
39+
"amount": "18000000",
40+
"destination": "rChargingStation",
41+
"intentHash": "c5b3fef249390716d89e732c4c38bdc07c424e85ecb0a2b69af1f9c5fc280ea1",
42+
"expiresAt": "2030-12-31T23:59:59Z",
43+
"nonce": "7284de37-62eb-4823-957d-a0c23fd73eeb"
44+
},
45+
"issuerKeyId": "fleet-spa-signing-key-1",
46+
"signature": "kAM7xStR3Zh8/8twk5B0OlMXo7r8AnEqzYAMgooDhTHIMwN0WtI4bob2axT3T4r8fpwelPA88fbSxVykd0TuDA=="
47+
},
48+
"sba": {
49+
"authorization": {
50+
"version": "1.0",
51+
"budgetId": "d26d4e94-dac4-4af6-81ad-7fd4080a2779",
52+
"grantId": "84fff5d0-8656-4b2d-ab82-15fa36427c38",
53+
"sessionId": "trip-20260314-EV001",
54+
"vehicleId": "EV-001",
55+
"policyHash": "a1b2c3d4e5f6",
56+
"currency": "USD",
57+
"minorUnit": 2,
58+
"budgetScope": "SESSION",
59+
"maxAmountMinor": "4000",
60+
"allowedRails": [
61+
"xrpl"
62+
],
63+
"allowedAssets": [
64+
{
65+
"kind": "IOU",
66+
"currency": "RLUSD",
67+
"issuer": "rIssuer"
68+
}
69+
],
70+
"destinationAllowlist": [
71+
"rTollBooth",
72+
"rChargingStation",
73+
"rParkingGarage"
74+
],
75+
"expiresAt": "2030-12-31T23:59:59Z"
76+
},
77+
"issuerKeyId": "fleet-sba-signing-key-1",
78+
"signature": "U3MIfbuyy2CE7PmQB7AAsJLV3+DpBcOEokXBnrcU7m5Ud9UhEPObEtxSy6yGPY3TTspdtLqJOJEz2i7lio88Dg=="
79+
},
80+
"policyGrant": {
81+
"grantId": "84fff5d0-8656-4b2d-ab82-15fa36427c38",
82+
"policyHash": "a1b2c3d4e5f6",
83+
"expiresAt": "2030-12-31T23:59:59Z",
84+
"allowedRails": [
85+
"xrpl"
86+
],
87+
"allowedAssets": [
88+
{
89+
"kind": "IOU",
90+
"currency": "RLUSD",
91+
"issuer": "rIssuer"
92+
}
93+
],
94+
"issuer": "did:web:fleet.robotaxi-west.example.com",
95+
"issuerKeyId": "fleet-policy-grant-key-1",
96+
"signature": "LoWlfxnzxWBaG0WmFhyBnITlujxETOE7f7OsM4KQ7IDYNyERnau8dG14vFt0DcrRthBTbFL+VMQPSMulV/VgCw=="
97+
},
98+
"paymentPolicyDecision": {
99+
"decisionId": "dec-charge-001",
100+
"policyHash": "a1b2c3d4e5f6",
101+
"action": "ALLOW",
102+
"reasons": [
103+
"Within budget",
104+
"Destination authorized"
105+
],
106+
"expiresAtISO": "2030-12-31T23:59:59Z",
107+
"rail": "xrpl",
108+
"asset": {
109+
"kind": "IOU",
110+
"currency": "RLUSD",
111+
"issuer": "rIssuer"
112+
},
113+
"priceFiat": {
114+
"amountMinor": "1800",
115+
"currency": "USD"
116+
},
117+
"chosen": {
118+
"rail": "xrpl",
119+
"quoteId": "q-charge-001"
120+
},
121+
"settlementQuotes": [
122+
{
123+
"quoteId": "q-charge-001",
124+
"rail": "xrpl",
125+
"amount": {
126+
"amount": "18000000",
127+
"decimals": 6
128+
},
129+
"destination": "rChargingStation",
130+
"expiresAt": "2030-12-31T23:59:59Z",
131+
"asset": {
132+
"kind": "IOU",
133+
"currency": "RLUSD",
134+
"issuer": "rIssuer"
135+
}
136+
}
137+
]
138+
},
139+
"sbaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAr7H9fidwL/o6BxBbWDp864CFpXpW4xehPl3LY3zMAw8=\n-----END PUBLIC KEY-----\n",
140+
"spaPublicKeyPem": "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEApNN61UHHMjipJXLvQ+gzZsGjjMznj3lGzwtJmhWitiw=\n-----END PUBLIC KEY-----\n"
141+
}

0 commit comments

Comments
 (0)