Skip to content

Commit 612229e

Browse files
naoryNAOR YUVALclaude
authored
feat(PR31): remove SPA and SettlementIntent — chain ends at SBA (#49)
* feat(PR30): add budgetMinor, budgetEscrowRef, authorizedGateway, offlineMaxSinglePayment to PolicyGrantLike XRPL-escrow budget enforcement requires these PA-signed fields in the PolicyGrant so the Trust Gateway can enforce ceilings from a tamper-proof source rather than trusting agent-reported values. - PolicyGrantLike: budgetMinor, budgetCurrency, budgetEscrowRef, authorizedGateway, offlineMaxSinglePayment, offlineMaxSinglePaymentCurrency - policyGrantForVerificationSchema: Zod validators for all six fields (numeric strings validated via regex) - createPolicyGrant / CreatePolicyGrantInput: factory and input type updated Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: review corrections — JSDoc consistency and rail-agnostic wording - types.ts: authorizedGateway — remove XRPL-specific "address" wording, use rail-agnostic description - types.ts: offlineMaxSinglePayment — "(drops)" → "minor units" (rail-agnostic) - types.ts: budgetEscrowRef example — add lockId to eth example for consistency with spec - createPolicyGrant.ts: add missing JSDoc for authorizedGateway, offlineMaxSinglePayment, offlineMaxSinglePaymentCurrency (other new fields had docs, these three did not) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(PR31): remove SPA and SettlementIntent — Trust Gateway is mandatory, chain ends at SBA The Trust Gateway is now a mandatory actor that submits XRPL payments directly. A separate SignedPaymentAuthorization (SPA) artifact is no longer needed. The authorization chain is simplified: PolicyGrant → SBA → [gateway verifies + submits XRPL tx] → receipt. Deleted: src/protocol/spa.ts, src/protocol/schema/paymentAuthorization.ts, src/protocol/schema/settlementIntent.ts, src/hash/computeSettlementIntentHash.ts, src/sdk/createSettlementIntent.ts, src/verifier/verifyPaymentAuthorization.ts, src/verifier/verifyDisputedSettlement.ts, src/verifier/verifySettlementIntent.ts. Updated: verifyPipeline, verifySettlement family, SettlementVerificationContext, VerificationReport (removed hashBindingChecked), CLI bundle/verify/formatReport, sdk/index, hash/index, protocol/schema/index, service/index, and all tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: NAOR YUVAL <naoryuval@NAORs-MacBook-Air.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 7db7386 commit 612229e

42 files changed

Lines changed: 115 additions & 2538 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/cli/bundle.ts

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,23 @@
11
import type {
22
PaymentPolicyDecision,
3-
SettlementResult,
43
} from "../policy-core/types.js";
54
import type { SignedSessionBudgetAuthorization } from "../protocol/sba.js";
6-
import type { SignedPaymentAuthorization } from "../protocol/spa.js";
75
import type { PolicyGrantLike } from "../verifier/types.js";
86
import type { SettlementVerificationContext } from "../verifier/types.js";
97

108
/**
119
* JSON artifact bundle format for CLI verification.
1210
* Alternative to full SettlementVerificationContext — artifacts keyed by type.
1311
*
14-
* Optional sbaPublicKeyPem and spaPublicKeyPem make the bundle self-contained:
15-
* verification can run without env vars when these are present.
12+
* Optional sbaPublicKeyPem makes the bundle self-contained:
13+
* verification can run without env vars when this is present.
1614
*/
1715
export interface SettlementBundle {
18-
settlement: SettlementResult;
19-
settlementIntent?: unknown;
20-
spa: SignedPaymentAuthorization;
2116
sba: SignedSessionBudgetAuthorization;
2217
policyGrant: PolicyGrantLike;
2318
paymentPolicyDecision?: PaymentPolicyDecision;
2419
/** PEM of SBA signing public key. When present, enables verify without MPCP_SBA_SIGNING_PUBLIC_KEY_PEM env. */
2520
sbaPublicKeyPem?: string;
26-
/** PEM of SPA signing public key. When present, enables verify without MPCP_SPA_SIGNING_PUBLIC_KEY_PEM env. */
27-
spaPublicKeyPem?: string;
2821
}
2922

3023
function isBundleLike(obj: unknown): obj is Record<string, unknown> {
@@ -37,59 +30,31 @@ function isBundleLike(obj: unknown): obj is Record<string, unknown> {
3730
export function isSettlementBundle(obj: unknown): obj is SettlementBundle {
3831
if (!isBundleLike(obj)) return false;
3932
return (
40-
"settlement" in obj &&
41-
"spa" in obj &&
4233
"sba" in obj &&
4334
"policyGrant" in obj &&
44-
!("signedBudgetAuthorization" in obj) &&
45-
!("signedPaymentAuthorization" in obj)
35+
!("signedBudgetAuthorization" in obj)
4636
);
4737
}
4838

49-
/**
50-
* Build a minimal PaymentPolicyDecision from SPA authorization.
51-
* Used when bundle omits paymentPolicyDecision.
52-
*/
53-
function decisionFromSpa(spa: SignedPaymentAuthorization): PaymentPolicyDecision & { _synthesized: true } {
54-
const a = spa.authorization;
55-
const quote = {
56-
quoteId: a.quoteId,
57-
rail: a.rail,
58-
amount: { amount: a.amount, decimals: 6 },
59-
destination: a.destination ?? "",
60-
expiresAt: a.expiresAt,
61-
asset: a.asset,
62-
};
63-
return {
64-
decisionId: a.decisionId,
65-
policyHash: a.policyHash,
66-
action: "ALLOW",
67-
reasons: ["OK"],
68-
expiresAtISO: a.expiresAt,
69-
rail: a.rail,
70-
asset: a.asset,
71-
chosen: { rail: a.rail, quoteId: a.quoteId },
72-
settlementQuotes: [quote],
73-
_synthesized: true,
74-
};
75-
}
76-
7739
/**
7840
* Convert a settlement bundle to SettlementVerificationContext.
7941
*/
8042
export function bundleToContext(bundle: SettlementBundle): SettlementVerificationContext {
81-
const decision =
82-
bundle.paymentPolicyDecision ?? decisionFromSpa(bundle.spa);
8343
if (!bundle.paymentPolicyDecision) {
84-
console.warn("[mpcp] Warning: paymentPolicyDecision absent — synthesized from SPA. Policy evaluation not verified.");
44+
console.warn("[mpcp] Warning: paymentPolicyDecision absent — budget policy evaluation not verified.");
8545
}
46+
const decision = bundle.paymentPolicyDecision ?? ({
47+
decisionId: "unknown",
48+
policyHash: bundle.sba.authorization.policyHash,
49+
action: "ALLOW",
50+
reasons: ["synthesized"] as unknown as import("../policy-core/types.js").PolicyReasonCode[],
51+
expiresAtISO: bundle.sba.authorization.expiresAt,
52+
rail: bundle.sba.authorization.allowedRails[0] ?? "xrpl",
53+
_synthesized: true,
54+
} as unknown as PaymentPolicyDecision & { _synthesized: true });
8655
return {
8756
policyGrant: bundle.policyGrant,
8857
signedBudgetAuthorization: bundle.sba,
89-
signedPaymentAuthorization: bundle.spa,
90-
settlement: bundle.settlement,
9158
paymentPolicyDecision: decision,
92-
decisionId: bundle.spa.authorization.decisionId,
93-
settlementIntent: bundle.settlementIntent ?? (bundle as { intent?: unknown }).intent,
9459
};
9560
}

src/cli/formatReport.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@
44
*/
55

66
import type { VerificationReport, VerificationStep } from "../verifier/types.js";
7-
import type { PaymentPolicyDecision } from "../policy-core/types.js";
87

98
const CHECK = "✔";
109
const CROSS = "✗";
1110

12-
/** Reverse execution order for chain display: leaf (intent) → root (grant). */
11+
/** Reverse execution order for chain display: leaf (budget) → root (grant). */
1312
const CHAIN_ORDER = [
14-
"SettlementIntent.intentHash",
15-
"SignedPaymentAuthorization.valid",
1613
"SignedBudgetAuthorization.valid",
1714
"PolicyGrant.valid",
1815
];
@@ -26,15 +23,10 @@ function orderSteps(steps: VerificationStep[]): VerificationStep[] {
2623
return ordered;
2724
}
2825

29-
export function formatVerificationReport(report: VerificationReport & { decision?: PaymentPolicyDecision & { _synthesized?: boolean } }): string {
26+
export function formatVerificationReport(report: VerificationReport): string {
3027
const lines: string[] = [];
3128
const ordered = orderSteps(report.steps);
3229

33-
if (report.decision?._synthesized) {
34-
lines.push("⚠ Policy decision: SYNTHESIZED FROM SPA (policy evaluation not verified)");
35-
lines.push("");
36-
}
37-
3830
for (const step of ordered) {
3931
const icon = step.ok ? CHECK : CROSS;
4032
const msg = step.ok ? step.name : `${step.name}: ${step.reason ?? "failed"}`;
@@ -43,12 +35,6 @@ export function formatVerificationReport(report: VerificationReport & { decision
4335

4436
if (ordered.length > 0) lines.push("");
4537

46-
if (report.hashBindingChecked === true) {
47-
lines.push("Hash binding: CHECKED");
48-
} else if (report.hashBindingChecked === false) {
49-
lines.push("Hash binding: NOT CHECKED (Lite Profile — intentHash absent)");
50-
}
51-
5238
if (report.result.valid) {
5339
lines.push("MPCP verification PASSED");
5440
} else {

src/cli/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ Verify options:
2626
2727
Accepts:
2828
- Full SettlementVerificationContext (policyGrant, signedBudgetAuthorization, etc.)
29-
- JSON artifact bundle: settlement, intent, spa, sba, policyGrant
29+
- JSON artifact bundle: sba, policyGrant
3030
3131
Examples:
3232
mpcp verify examples/parking/settlement-bundle.json

src/cli/verify.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ export function runVerify(
7070
process.env.MPCP_SBA_SIGNING_PUBLIC_KEY_PEM = data.sbaPublicKeyPem;
7171
process.env.MPCP_SBA_SIGNING_KEY_ID = data.sba.issuerKeyId;
7272
}
73-
if (data.spaPublicKeyPem) {
74-
saved.MPCP_SPA_SIGNING_PUBLIC_KEY_PEM = process.env.MPCP_SPA_SIGNING_PUBLIC_KEY_PEM;
75-
saved.MPCP_SPA_SIGNING_KEY_ID = process.env.MPCP_SPA_SIGNING_KEY_ID;
76-
process.env.MPCP_SPA_SIGNING_PUBLIC_KEY_PEM = data.spaPublicKeyPem;
77-
process.env.MPCP_SPA_SIGNING_KEY_ID = data.spa.issuerKeyId;
78-
}
7973
}
8074

8175
try {

src/hash/computeSettlementIntentHash.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/hash/index.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,2 @@
11
export { canonicalJson } from "./canonicalJson.js";
22
export { sha256Hex } from "./sha256.js";
3-
export { computeSettlementIntentHash } from "./computeSettlementIntentHash.js";
4-
5-
/** @deprecated Use computeSettlementIntentHash instead */
6-
export { computeSettlementIntentHash as computeIntentHash } from "./computeSettlementIntentHash.js";

src/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export * from "./anchor/index.js";
22
export * from "./policy-core/index.js";
33
export * from "./protocol/types.js";
4-
export * from "./protocol/spa.js";
54
export * from "./protocol/sba.js";
65
export * from "./hash/index.js";
76
export * from "./sdk/index.js";

src/protocol/schema/artifactBundle.ts

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,23 @@
11
import { z } from "zod";
2-
import { railSchema, assetSchema, iso8601DatetimeSchema } from "./shared.js";
32
import { signedBudgetAuthorizationSchema } from "./signedBudgetAuthorization.js";
4-
import { signedPaymentAuthorizationSchema } from "./paymentAuthorization.js";
53
import { policyGrantForVerificationSchema } from "./verifySchemas.js";
6-
import { settlementIntentForVerificationSchema } from "./verifySchemas.js";
7-
8-
/**
9-
* Settlement result (executed settlement) for verification.
10-
* Rail-specific fields (txHash, payer, etc.) may be present.
11-
*/
12-
export const settlementResultSchema = z
13-
.object({
14-
amount: z.string(),
15-
rail: railSchema,
16-
asset: assetSchema.optional(),
17-
destination: z.string().optional(),
18-
nowISO: iso8601DatetimeSchema.optional(),
19-
})
20-
.passthrough();
21-
22-
export type SettlementResultSchema = z.infer<typeof settlementResultSchema>;
234

245
/**
256
* Canonical MPCP artifact bundle format.
26-
* Packages complete payment verification data for exchange between systems.
7+
* Packages complete authorization verification data for exchange between systems.
278
*
28-
* Required: policyGrant, sba, spa, settlement
29-
* Optional: settlementIntent, paymentPolicyDecision, ledgerAnchor, public keys
9+
* Required: policyGrant, sba
10+
* Optional: paymentPolicyDecision, public key
3011
*
31-
* When sbaPublicKeyPem and spaPublicKeyPem are present, the bundle is self-contained
32-
* and verification can run without MPCP_*_SIGNING_PUBLIC_KEY_PEM env vars.
12+
* When sbaPublicKeyPem is present, the bundle is self-contained
13+
* and verification can run without MPCP_SBA_SIGNING_PUBLIC_KEY_PEM env var.
3314
*/
3415
export const artifactBundleSchema = z
3516
.object({
3617
policyGrant: policyGrantForVerificationSchema,
3718
sba: signedBudgetAuthorizationSchema,
38-
spa: signedPaymentAuthorizationSchema,
39-
settlement: settlementResultSchema,
40-
settlementIntent: settlementIntentForVerificationSchema.optional(),
4119
paymentPolicyDecision: z.record(z.unknown()).optional(),
42-
ledgerAnchor: z.record(z.unknown()).optional(),
4320
sbaPublicKeyPem: z.string().optional(),
44-
spaPublicKeyPem: z.string().optional(),
4521
})
4622
.strict();
4723

src/protocol/schema/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ export * from "./artifactBundle.js";
33
export * from "./policyGrant.js";
44
export * from "./budgetAuthorization.js";
55
export * from "./signedBudgetAuthorization.js";
6-
export * from "./paymentAuthorization.js";
7-
export * from "./settlementIntent.js";
86
export * from "./fleetPolicyAuthorization.js";
97
export * from "./verifySchemas.js";
108
export { validateWithSchema } from "./validate.js";

src/protocol/schema/paymentAuthorization.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)