xytara 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +29 -0
- package/BSV_TERANODE_SETUP.md +43 -0
- package/README.md +66 -7
- package/REAL_PAYMENT_SETUP.md +61 -0
- package/RELEASE_NOTES.md +12 -6
- package/SERVICE_CONTRACT.md +31 -0
- package/START_HERE.md +107 -6
- package/TREASURY_DESTINATIONS.example.json +66 -0
- package/TREASURY_DESTINATIONS.production.template.json +108 -0
- package/adapters/examples/langchain-reference-execution-adapter.js +81 -0
- package/adapters/examples/langchain-reference-execution-adapter.manifest.json +79 -0
- package/adapters/examples/langgraph-reference-execution-adapter.js +81 -0
- package/adapters/examples/langgraph-reference-execution-adapter.manifest.json +79 -0
- package/bin/xytara-first-run.js +244 -0
- package/bin/xytara.js +30 -5
- package/integrations/dispatch.js +7 -2
- package/integrations/registry.js +123 -2
- package/lib/activity_audit_contract.js +322 -0
- package/lib/adapter_depth_contract.js +156 -0
- package/lib/bolt_lane_contract.js +55 -0
- package/lib/bolt_payment_front.js +172 -0
- package/lib/capability_execution_truth.js +269 -0
- package/lib/capability_registry.js +39 -2
- package/lib/checkout_event_security.js +142 -0
- package/lib/command_flow.js +169 -36
- package/lib/commerce_artifacts.js +23 -4
- package/lib/commerce_checkout.js +134 -4
- package/lib/commerce_client.js +20 -0
- package/lib/commerce_economics.js +109 -9
- package/lib/commerce_runtime.js +38 -2
- package/lib/commerce_shell.js +4 -2
- package/lib/external_execution_runtime.js +132 -0
- package/lib/go_live_operator_pack.js +339 -0
- package/lib/http_transport.js +32 -0
- package/lib/integration_matrix_contract.js +6 -0
- package/lib/l402_lane_contract.js +55 -0
- package/lib/l402_payment_front.js +192 -0
- package/lib/network_transport.js +110 -0
- package/lib/operator_intelligence.js +215 -1
- package/lib/payment_config.js +80 -3
- package/lib/payment_front_registry.js +53 -0
- package/lib/payment_protocol_contract.js +165 -0
- package/lib/payment_verification.js +23 -3
- package/lib/pricing_optimization_contract.js +556 -0
- package/lib/release_history.js +15 -0
- package/lib/request_rate_limit.js +144 -0
- package/lib/runtime_bridge.js +10 -2
- package/lib/runtime_operations_worker.js +282 -0
- package/lib/settlement_bsv_live.js +79 -4
- package/lib/stripe_mpp_lane_contract.js +3 -0
- package/lib/stripe_mpp_payment_front.js +158 -0
- package/lib/treasury_destinations_contract.js +543 -0
- package/lib/treasury_egress_policy_contract.js +91 -0
- package/package.json +14 -21
- package/server.js +627 -88
- package/OPERATIONS_RUNBOOK.md +0 -66
- package/OPERATOR_START_HERE.md +0 -63
- package/PROGRAM_COMPLETE_RELEASE.md +0 -63
- package/PROGRAM_STATUS.md +0 -57
- package/PUBLISH_PLAN.md +0 -14
- package/RELEASE_CHECKLIST.md +0 -16
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const packageJson = require("../package.json");
|
|
4
|
+
const {
|
|
5
|
+
buildCapabilityExecutionTruthBundle,
|
|
6
|
+
buildCapabilityCommercializationSummary
|
|
7
|
+
} = require("./capability_execution_truth");
|
|
8
|
+
const { buildPaymentProtocolStrategyPack } = require("./payment_protocol_contract");
|
|
9
|
+
const { summarizeTreasuryDestinationsPack } = require("./treasury_destinations_contract");
|
|
10
|
+
const { loadExecutionTargetRegistry } = require("./external_execution_runtime");
|
|
11
|
+
const { buildCheckoutEventSecuritySummary } = require("./checkout_event_security");
|
|
12
|
+
const {
|
|
13
|
+
buildOperatorIntelligencePack,
|
|
14
|
+
summarizeOperatorIntelligencePack
|
|
15
|
+
} = require("./operator_intelligence");
|
|
16
|
+
|
|
17
|
+
function normalizeFunction(fn, fallback) {
|
|
18
|
+
return typeof fn === "function" ? fn : fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function buildDefaultRuntimeDurability() {
|
|
22
|
+
return {
|
|
23
|
+
ok: true,
|
|
24
|
+
recovery_posture: "unknown",
|
|
25
|
+
blockers: ["runtime_durability_unavailable"],
|
|
26
|
+
operator_guidance: "inspect /v1/runtime/durability from the live service to confirm persistence posture"
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function buildDefaultSettlementReadiness() {
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
ready: false,
|
|
34
|
+
runtime_mode: "unknown",
|
|
35
|
+
blockers: ["native_settlement_readiness_unavailable"],
|
|
36
|
+
next_manual_inputs: ["inspect_live_service_settlement_readiness"]
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function rankBlockerSeverity(blocker) {
|
|
41
|
+
if (typeof blocker !== "string" || blocker.length === 0) return "medium";
|
|
42
|
+
if (
|
|
43
|
+
blocker.includes("missing")
|
|
44
|
+
|| blocker.includes("failed")
|
|
45
|
+
|| blocker.includes("unreachable")
|
|
46
|
+
|| blocker.includes("not_ready")
|
|
47
|
+
|| blocker.includes("not_writable")
|
|
48
|
+
|| blocker.includes("mock")
|
|
49
|
+
|| blocker.includes("too_weak")
|
|
50
|
+
) {
|
|
51
|
+
return "high";
|
|
52
|
+
}
|
|
53
|
+
return "medium";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildBlockingIssues(executionTruth, commercialization, runtimeDurability, settlementReadiness, treasurySummary, checkoutSecurity) {
|
|
57
|
+
const issues = [];
|
|
58
|
+
|
|
59
|
+
(Array.isArray(runtimeDurability.blockers) ? runtimeDurability.blockers : []).forEach((blocker) => {
|
|
60
|
+
issues.push({
|
|
61
|
+
lane: "runtime_durability",
|
|
62
|
+
severity: rankBlockerSeverity(blocker),
|
|
63
|
+
blocker,
|
|
64
|
+
reason: runtimeDurability.operator_guidance || "runtime durability requires operator review",
|
|
65
|
+
ref: "/v1/runtime/durability"
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
(Array.isArray(settlementReadiness.blockers) ? settlementReadiness.blockers : []).forEach((blocker) => {
|
|
70
|
+
issues.push({
|
|
71
|
+
lane: "native_settlement",
|
|
72
|
+
severity: rankBlockerSeverity(blocker),
|
|
73
|
+
blocker,
|
|
74
|
+
reason: "native settlement readiness still has blocking configuration gaps",
|
|
75
|
+
ref: "/v1/settlement/bsv-teranode/readiness"
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (Number(treasurySummary.configured_profile_count || 0) < 1) {
|
|
80
|
+
issues.push({
|
|
81
|
+
lane: "treasury_destinations",
|
|
82
|
+
severity: "high",
|
|
83
|
+
blocker: "treasury_destinations_unconfigured",
|
|
84
|
+
reason: "no treasury destination profile is configured with a real landing reference",
|
|
85
|
+
ref: "/v1/treasury-destinations"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (Number(executionTruth.counts.provider_wiring_required_count || 0) > 0) {
|
|
90
|
+
issues.push({
|
|
91
|
+
lane: "provider_execution",
|
|
92
|
+
severity: "high",
|
|
93
|
+
blocker: "provider_execution_targets_missing",
|
|
94
|
+
reason: `${executionTruth.counts.provider_wiring_required_count} capability lane(s) still need execution target bindings`,
|
|
95
|
+
ref: "/v1/capability-registry/execution-truth"
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (Number(executionTruth.counts.operator_classification_required_count || 0) > 0) {
|
|
100
|
+
issues.push({
|
|
101
|
+
lane: "capability_classification",
|
|
102
|
+
severity: "medium",
|
|
103
|
+
blocker: "capability_classification_incomplete",
|
|
104
|
+
reason: `${executionTruth.counts.operator_classification_required_count} capability lane(s) still require execution classification`,
|
|
105
|
+
ref: "/v1/capability-registry/execution-truth"
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (Number(commercialization.counts.hold_until_classified || 0) > 0) {
|
|
110
|
+
issues.push({
|
|
111
|
+
lane: "commercialization",
|
|
112
|
+
severity: "medium",
|
|
113
|
+
blocker: "catalog_items_not_ready_for_public_sale",
|
|
114
|
+
reason: `${commercialization.counts.hold_until_classified} capability lane(s) should stay off live public sale surfaces`,
|
|
115
|
+
ref: "/v1/capability-registry/commercialization-summary"
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
(Array.isArray(checkoutSecurity.blockers) ? checkoutSecurity.blockers : []).forEach((blocker) => {
|
|
120
|
+
issues.push({
|
|
121
|
+
lane: "hosted_checkout_ingress",
|
|
122
|
+
severity: rankBlockerSeverity(blocker),
|
|
123
|
+
blocker,
|
|
124
|
+
reason: checkoutSecurity.operator_guidance || "hosted checkout ingress security requires operator review",
|
|
125
|
+
ref: "/v1/checkout/events/security"
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return issues;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildNextActions(blockingIssues, operatorIntelligence, executionTruth, treasurySummary, settlementReadiness) {
|
|
133
|
+
const actions = [];
|
|
134
|
+
|
|
135
|
+
if (blockingIssues.some((issue) => issue.blocker === "treasury_destinations_unconfigured")) {
|
|
136
|
+
actions.push({
|
|
137
|
+
priority: "high",
|
|
138
|
+
action: "configure_treasury_destinations",
|
|
139
|
+
reason: "real money should not land until treasury destinations are explicitly configured",
|
|
140
|
+
refs: [
|
|
141
|
+
"/v1/treasury-destinations",
|
|
142
|
+
"/v1/treasury-destinations/operator-pack"
|
|
143
|
+
]
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (blockingIssues.some((issue) => issue.blocker === "provider_execution_targets_missing")) {
|
|
148
|
+
const providerRequiredTasks = (Array.isArray(executionTruth.tasks) ? executionTruth.tasks : [])
|
|
149
|
+
.filter((task) => task.execution_truth === "provider_wiring_required")
|
|
150
|
+
.map((task) => task.task_ref);
|
|
151
|
+
actions.push({
|
|
152
|
+
priority: "high",
|
|
153
|
+
action: "bind_provider_execution_targets",
|
|
154
|
+
reason: `provider-backed execution is still missing for ${providerRequiredTasks.join(", ") || "one or more capability lanes"}`,
|
|
155
|
+
refs: [
|
|
156
|
+
"/v1/capability-registry/execution-truth",
|
|
157
|
+
"/v1/capability-registry/commercialization-summary"
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (settlementReadiness.ready !== true) {
|
|
163
|
+
actions.push({
|
|
164
|
+
priority: "high",
|
|
165
|
+
action: "close_native_settlement_inputs",
|
|
166
|
+
reason: `native settlement readiness is blocked by ${(settlementReadiness.blockers || []).join(", ") || "unresolved inputs"}`,
|
|
167
|
+
refs: [
|
|
168
|
+
"/v1/settlement/bsv-teranode/readiness",
|
|
169
|
+
"/v1/settlement/bsv-teranode"
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (blockingIssues.some((issue) => issue.lane === "hosted_checkout_ingress")) {
|
|
175
|
+
actions.push({
|
|
176
|
+
priority: "high",
|
|
177
|
+
action: "configure_checkout_webhook_verification",
|
|
178
|
+
reason: "hosted checkout event ingress should be signed before xytara trusts payment-state transitions",
|
|
179
|
+
refs: [
|
|
180
|
+
"/v1/checkout/events/security",
|
|
181
|
+
"/v1/stripe-mpp"
|
|
182
|
+
]
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (Number(treasurySummary.explicit_per_rail_configured_count || 0) < 1) {
|
|
187
|
+
actions.push({
|
|
188
|
+
priority: "medium",
|
|
189
|
+
action: "upgrade_from_fallback_to_per_rail_treasury_profiles",
|
|
190
|
+
reason: "explicit per-rail treasury profiles strengthen reconciliation and public claim integrity",
|
|
191
|
+
refs: [
|
|
192
|
+
"/v1/treasury-destinations",
|
|
193
|
+
"/v1/treasury-destinations/operator-pack"
|
|
194
|
+
]
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (operatorIntelligence && operatorIntelligence.recommended_operator_motion) {
|
|
199
|
+
actions.push({
|
|
200
|
+
priority: operatorIntelligence.overall_operator_state && operatorIntelligence.overall_operator_state.top_priority
|
|
201
|
+
? operatorIntelligence.overall_operator_state.top_priority
|
|
202
|
+
: "medium",
|
|
203
|
+
action: operatorIntelligence.recommended_operator_motion,
|
|
204
|
+
reason: "current operator intelligence indicates this as the next best runtime or adoption move",
|
|
205
|
+
refs: [
|
|
206
|
+
"/v1/operator-intelligence",
|
|
207
|
+
`/v1/economics/accounts/${encodeURIComponent(operatorIntelligence.account_id || "acct_demo")}/operator-dashboard`
|
|
208
|
+
]
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (actions.length === 0) {
|
|
213
|
+
actions.push({
|
|
214
|
+
priority: "low",
|
|
215
|
+
action: "run_first_live_money_flow",
|
|
216
|
+
reason: "core launch blockers are cleared enough to run a first live end-to-end production transaction",
|
|
217
|
+
refs: [
|
|
218
|
+
"/v1/payment-ledger",
|
|
219
|
+
"/v1/activity-ledger",
|
|
220
|
+
"/v1/reconciliation-report"
|
|
221
|
+
]
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return actions;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function buildGoLiveOperatorPack(state, input, options) {
|
|
229
|
+
const payload = input && typeof input === "object" && !Array.isArray(input) ? input : {};
|
|
230
|
+
const accountId = String(payload.account_id || "acct_demo").trim() || "acct_demo";
|
|
231
|
+
const runtimeDurabilityBuilder = normalizeFunction(options && options.buildRuntimeDurabilitySummary, buildDefaultRuntimeDurability);
|
|
232
|
+
const settlementReadinessBuilder = normalizeFunction(options && options.buildNativeSettlementReadinessSummary, buildDefaultSettlementReadiness);
|
|
233
|
+
|
|
234
|
+
const executionTruth = buildCapabilityExecutionTruthBundle();
|
|
235
|
+
const commercialization = buildCapabilityCommercializationSummary();
|
|
236
|
+
const paymentStrategy = buildPaymentProtocolStrategyPack();
|
|
237
|
+
const treasurySummary = summarizeTreasuryDestinationsPack();
|
|
238
|
+
const checkoutSecurity = buildCheckoutEventSecuritySummary();
|
|
239
|
+
const runtimeDurability = runtimeDurabilityBuilder();
|
|
240
|
+
const settlementReadiness = settlementReadinessBuilder();
|
|
241
|
+
const operatorIntelligence = buildOperatorIntelligencePack(state, { account_id: accountId });
|
|
242
|
+
const executionTargets = loadExecutionTargetRegistry();
|
|
243
|
+
const blockingIssues = buildBlockingIssues(
|
|
244
|
+
executionTruth,
|
|
245
|
+
commercialization,
|
|
246
|
+
runtimeDurability,
|
|
247
|
+
settlementReadiness,
|
|
248
|
+
treasurySummary,
|
|
249
|
+
checkoutSecurity
|
|
250
|
+
);
|
|
251
|
+
const nextActions = buildNextActions(
|
|
252
|
+
blockingIssues,
|
|
253
|
+
operatorIntelligence,
|
|
254
|
+
executionTruth,
|
|
255
|
+
treasurySummary,
|
|
256
|
+
settlementReadiness
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
ok: true,
|
|
261
|
+
product: packageJson.name,
|
|
262
|
+
category: "machine-commerce-go-live-operator-pack",
|
|
263
|
+
pack_version: "xytara-go-live-operator-pack-v1",
|
|
264
|
+
account_id: accountId,
|
|
265
|
+
go_live_state: blockingIssues.length === 0 ? "launch_ready_for_first_live_money_flow" : "launch_blocked_pending_operator_closeout",
|
|
266
|
+
launch_posture: {
|
|
267
|
+
public_capability_count: executionTruth.counts.task_count,
|
|
268
|
+
self_serve_live_now_count: commercialization.counts.self_serve_live_now,
|
|
269
|
+
operator_enabled_after_config_count: commercialization.counts.operator_enabled_after_config,
|
|
270
|
+
configured_execution_target_count: executionTargets.length,
|
|
271
|
+
configured_treasury_profile_count: treasurySummary.configured_profile_count,
|
|
272
|
+
explicit_per_rail_treasury_profile_count: treasurySummary.explicit_per_rail_configured_count,
|
|
273
|
+
runtime_recovery_posture: runtimeDurability.recovery_posture,
|
|
274
|
+
native_settlement_ready: settlementReadiness.ready === true,
|
|
275
|
+
checkout_ingress_security_ready: checkoutSecurity.ready === true
|
|
276
|
+
},
|
|
277
|
+
payment_posture: {
|
|
278
|
+
machine_primary: paymentStrategy.primary_machine_payment_front || null,
|
|
279
|
+
repeat_use_primary: paymentStrategy.primary_repeat_use_payment_front || null,
|
|
280
|
+
human_primary: paymentStrategy.human_and_enterprise_procurement_front || null,
|
|
281
|
+
external_front_count: Array.isArray(paymentStrategy.adapter_ready_external_fronts)
|
|
282
|
+
? paymentStrategy.adapter_ready_external_fronts.length
|
|
283
|
+
: 0
|
|
284
|
+
},
|
|
285
|
+
blocking_issues: blockingIssues,
|
|
286
|
+
next_actions: nextActions,
|
|
287
|
+
launch_sequence: [
|
|
288
|
+
"configure_treasury_and_wallet_security",
|
|
289
|
+
"bind_provider_execution_targets_for_external_capability_lanes",
|
|
290
|
+
"close_native_settlement_readiness_inputs",
|
|
291
|
+
"confirm_persistent_runtime_storage_and_recovery",
|
|
292
|
+
"run_first_live_end_to_end_money_flow",
|
|
293
|
+
"watch_activity_payment_and_reconciliation_surfaces"
|
|
294
|
+
],
|
|
295
|
+
linked_surfaces: {
|
|
296
|
+
capability_execution_truth_ref: "/v1/capability-registry/execution-truth",
|
|
297
|
+
commercialization_summary_ref: "/v1/capability-registry/commercialization-summary",
|
|
298
|
+
payment_strategy_ref: "/v1/payment-protocols/strategy",
|
|
299
|
+
payment_fronts_ref: "/v1/payment-fronts",
|
|
300
|
+
checkout_event_security_ref: "/v1/checkout/events/security",
|
|
301
|
+
treasury_ref: "/v1/treasury-destinations",
|
|
302
|
+
native_settlement_ref: "/v1/settlement/bsv-teranode/readiness",
|
|
303
|
+
runtime_durability_ref: "/v1/runtime/durability",
|
|
304
|
+
operator_intelligence_ref: "/v1/operator-intelligence",
|
|
305
|
+
payment_ledger_ref: "/v1/payment-ledger",
|
|
306
|
+
reconciliation_ref: "/v1/reconciliation-report"
|
|
307
|
+
},
|
|
308
|
+
supporting_summaries: {
|
|
309
|
+
capability_counts: executionTruth.counts,
|
|
310
|
+
commercialization_counts: commercialization.counts,
|
|
311
|
+
treasury_summary: treasurySummary,
|
|
312
|
+
checkout_event_security: checkoutSecurity,
|
|
313
|
+
runtime_durability: runtimeDurability,
|
|
314
|
+
native_settlement: settlementReadiness,
|
|
315
|
+
operator_intelligence: summarizeOperatorIntelligencePack(state, { account_id: accountId })
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function summarizeGoLiveOperatorPack(state, input, options) {
|
|
321
|
+
const pack = buildGoLiveOperatorPack(state, input, options);
|
|
322
|
+
return {
|
|
323
|
+
ok: true,
|
|
324
|
+
product: pack.product,
|
|
325
|
+
category: pack.category,
|
|
326
|
+
pack_version: pack.pack_version,
|
|
327
|
+
account_id: pack.account_id,
|
|
328
|
+
go_live_state: pack.go_live_state,
|
|
329
|
+
blocking_issue_count: pack.blocking_issues.length,
|
|
330
|
+
top_action: pack.next_actions[0] || null,
|
|
331
|
+
launch_posture: pack.launch_posture,
|
|
332
|
+
linked_surfaces: pack.linked_surfaces
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
module.exports = {
|
|
337
|
+
buildGoLiveOperatorPack,
|
|
338
|
+
summarizeGoLiveOperatorPack
|
|
339
|
+
};
|
package/lib/http_transport.js
CHANGED
|
@@ -34,7 +34,39 @@ function readJsonBody(req) {
|
|
|
34
34
|
});
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function readJsonBodyWithRaw(req) {
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
let body = "";
|
|
40
|
+
req.on("data", (chunk) => {
|
|
41
|
+
body += chunk;
|
|
42
|
+
if (body.length > 1024 * 1024) {
|
|
43
|
+
reject(new Error("request body too large"));
|
|
44
|
+
req.destroy();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
req.on("end", () => {
|
|
48
|
+
if (!body) {
|
|
49
|
+
resolve({
|
|
50
|
+
body: {},
|
|
51
|
+
raw_body: ""
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
resolve({
|
|
57
|
+
body: JSON.parse(body),
|
|
58
|
+
raw_body: body
|
|
59
|
+
});
|
|
60
|
+
} catch (error) {
|
|
61
|
+
reject(new Error("body must be valid JSON"));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
req.on("error", reject);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
37
68
|
module.exports = {
|
|
38
69
|
readJsonBody,
|
|
70
|
+
readJsonBodyWithRaw,
|
|
39
71
|
sendJson
|
|
40
72
|
};
|
|
@@ -7,6 +7,8 @@ const { summarizeMcpLanePack } = require("./mcp_lane_contract");
|
|
|
7
7
|
const { summarizeA2ALanePack } = require("./a2a_lane_contract");
|
|
8
8
|
const { summarizeA2CLanePack } = require("./a2c_lane_contract");
|
|
9
9
|
const { summarizeX402LanePack } = require("./x402_lane_contract");
|
|
10
|
+
const { summarizeL402LanePack } = require("./l402_lane_contract");
|
|
11
|
+
const { summarizeBoltLanePack } = require("./bolt_lane_contract");
|
|
10
12
|
const { summarizeSettlementLanePack } = require("./settlement_lane_contract");
|
|
11
13
|
const { summarizeStablecoinLanePack } = require("./stablecoin_lane_contract");
|
|
12
14
|
const { summarizeMajorRailsLanePack } = require("./major_rails_lane_contract");
|
|
@@ -41,6 +43,8 @@ function buildIntegrationMatrixPack(integrations = []) {
|
|
|
41
43
|
a2a: summarizeA2ALanePack(),
|
|
42
44
|
a2c: summarizeA2CLanePack(),
|
|
43
45
|
x402: summarizeX402LanePack(),
|
|
46
|
+
l402: summarizeL402LanePack(),
|
|
47
|
+
bolt: summarizeBoltLanePack(),
|
|
44
48
|
settlement: summarizeSettlementLanePack(),
|
|
45
49
|
stablecoins: summarizeStablecoinLanePack(),
|
|
46
50
|
major_rails: summarizeMajorRailsLanePack(),
|
|
@@ -60,6 +64,8 @@ function buildIntegrationMatrixPack(integrations = []) {
|
|
|
60
64
|
framework_lane_ref: "/v1/frameworks",
|
|
61
65
|
protocol_lane_ref: "/v1/protocols",
|
|
62
66
|
mcp_lane_ref: "/v1/mcp",
|
|
67
|
+
l402_lane_ref: "/v1/l402",
|
|
68
|
+
bolt_lane_ref: "/v1/bolt",
|
|
63
69
|
settlement_lane_ref: "/v1/settlement",
|
|
64
70
|
event_system_lane_ref: "/v1/event-systems",
|
|
65
71
|
telemetry_lane_ref: "/v1/telemetry",
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function buildL402LanePack() {
|
|
4
|
+
return {
|
|
5
|
+
product: "xytara",
|
|
6
|
+
category: "machine-commerce-l402-lane-pack",
|
|
7
|
+
pack_version: "xytara-l402-lane-pack-v1",
|
|
8
|
+
lane_state: "adapter_front_contract_present",
|
|
9
|
+
posture: "lightning_plus_http_402_permissionless_payment_front",
|
|
10
|
+
settlement_family: "bitcoin_lightning",
|
|
11
|
+
auth_family: "macaroon_scoped_access",
|
|
12
|
+
role_in_xytara: "optional_permissionless_machine_payment_front",
|
|
13
|
+
supported_surfaces: {
|
|
14
|
+
security_ref: "/v1/l402/security",
|
|
15
|
+
commands_execute_ref: "/l402/commands/execute",
|
|
16
|
+
mcp_invoke_ref: "/l402/mcp/tools/invoke",
|
|
17
|
+
payment_protocols_ref: "/v1/payment-protocols",
|
|
18
|
+
payment_protocol_strategy_ref: "/v1/payment-protocols/strategy",
|
|
19
|
+
capability_commercialization_ref: "/v1/capability-registry/commercialization-summary",
|
|
20
|
+
execution_truth_ref: "/v1/capability-registry/execution-truth"
|
|
21
|
+
},
|
|
22
|
+
integration_rules: [
|
|
23
|
+
"use L402 as an adapter payment front into the same execution, proof, and governance core",
|
|
24
|
+
"do not fork runtime semantics by payment front",
|
|
25
|
+
"bind Lightning-native verification and access issuance through an adapter or gateway layer"
|
|
26
|
+
],
|
|
27
|
+
recommended_uses: [
|
|
28
|
+
"permission-sensitive agents",
|
|
29
|
+
"micropayment-heavy workloads",
|
|
30
|
+
"operators who want lower dependency on card or stablecoin facilitators"
|
|
31
|
+
],
|
|
32
|
+
non_claims: [
|
|
33
|
+
"not claimed as the launch-default machine payment lane",
|
|
34
|
+
"not claimed as a built-in first-party Lightning settlement runtime today"
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function summarizeL402LanePack() {
|
|
40
|
+
const pack = buildL402LanePack();
|
|
41
|
+
return {
|
|
42
|
+
product: pack.product,
|
|
43
|
+
category: pack.category,
|
|
44
|
+
summary_version: "xytara-l402-lane-pack-summary-v1",
|
|
45
|
+
lane_state: pack.lane_state,
|
|
46
|
+
posture: pack.posture,
|
|
47
|
+
supported_surface_count: Object.keys(pack.supported_surfaces).length,
|
|
48
|
+
linked_surfaces: pack.supported_surfaces
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
buildL402LanePack,
|
|
54
|
+
summarizeL402LanePack
|
|
55
|
+
};
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
|
|
5
|
+
function normalizeString(value) {
|
|
6
|
+
return typeof value === "string" ? value.trim() : "";
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function safeEqualHex(left, right) {
|
|
10
|
+
const a = Buffer.from(String(left || ""), "hex");
|
|
11
|
+
const b = Buffer.from(String(right || ""), "hex");
|
|
12
|
+
if (a.length === 0 || b.length === 0 || a.length !== b.length) return false;
|
|
13
|
+
return crypto.timingSafeEqual(a, b);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function decodeBase64UrlJson(value) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(Buffer.from(String(value || ""), "base64url").toString("utf8"));
|
|
19
|
+
} catch (_) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function loadL402FrontConfig() {
|
|
25
|
+
const sharedSecret = normalizeString(process.env.XYTARA_L402_SHARED_SECRET);
|
|
26
|
+
return {
|
|
27
|
+
enabled: String(process.env.XYTARA_L402_ENABLED || (sharedSecret ? "true" : "false")).trim().toLowerCase() !== "false",
|
|
28
|
+
shared_secret: sharedSecret || null,
|
|
29
|
+
max_age_seconds: Math.max(30, Number(process.env.XYTARA_L402_MAX_AGE_SECONDS || 300) || 300),
|
|
30
|
+
required_headers: ["authorization"],
|
|
31
|
+
auth_scheme: "L402",
|
|
32
|
+
token_shape: "base64url_json_with_macaroon_and_hmac_signed_payload"
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseL402Authorization(headers) {
|
|
37
|
+
const authorization = headers && typeof headers.authorization === "string"
|
|
38
|
+
? headers.authorization.trim()
|
|
39
|
+
: "";
|
|
40
|
+
const match = authorization.match(/^L402\s+(.+)$/i);
|
|
41
|
+
if (!match) return null;
|
|
42
|
+
const decoded = decodeBase64UrlJson(match[1]);
|
|
43
|
+
if (!decoded || typeof decoded !== "object") return null;
|
|
44
|
+
return {
|
|
45
|
+
wallet_id: decoded.wallet_id || decoded.gateway_id || "l402_gateway",
|
|
46
|
+
encoded: decoded.payload || null,
|
|
47
|
+
signature: decoded.signature || null,
|
|
48
|
+
macaroon: decoded.macaroon || null,
|
|
49
|
+
invoice_id: decoded.invoice_id || null,
|
|
50
|
+
preimage_hash: decoded.preimage_hash || null,
|
|
51
|
+
scheme: "l402"
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function verifyL402PaymentPayload(paymentPayload, quote, walletId, maxAgeSeconds) {
|
|
56
|
+
if (!paymentPayload || typeof paymentPayload !== "object") {
|
|
57
|
+
return { ok: false, reason: "l402_payload_invalid" };
|
|
58
|
+
}
|
|
59
|
+
if (!paymentPayload.quote_id || paymentPayload.quote_id !== quote.quote_id) {
|
|
60
|
+
return { ok: false, reason: "l402_quote_mismatch" };
|
|
61
|
+
}
|
|
62
|
+
if (paymentPayload.amount_minor !== quote.amount_minor) {
|
|
63
|
+
return { ok: false, reason: "l402_amount_mismatch" };
|
|
64
|
+
}
|
|
65
|
+
if (paymentPayload.currency !== quote.currency) {
|
|
66
|
+
return { ok: false, reason: "l402_currency_mismatch" };
|
|
67
|
+
}
|
|
68
|
+
if (paymentPayload.account_id && paymentPayload.account_id !== quote.account_id) {
|
|
69
|
+
return { ok: false, reason: "l402_account_mismatch" };
|
|
70
|
+
}
|
|
71
|
+
if (walletId && paymentPayload.wallet_id && paymentPayload.wallet_id !== walletId) {
|
|
72
|
+
return { ok: false, reason: "l402_wallet_mismatch" };
|
|
73
|
+
}
|
|
74
|
+
if (!paymentPayload.nonce || typeof paymentPayload.nonce !== "string") {
|
|
75
|
+
return { ok: false, reason: "l402_nonce_missing" };
|
|
76
|
+
}
|
|
77
|
+
if (!paymentPayload.issued_at_iso || Number.isNaN(Date.parse(paymentPayload.issued_at_iso))) {
|
|
78
|
+
return { ok: false, reason: "l402_issued_at_invalid" };
|
|
79
|
+
}
|
|
80
|
+
const ageSeconds = Math.max(0, Math.floor((Date.now() - Date.parse(paymentPayload.issued_at_iso)) / 1000));
|
|
81
|
+
if (ageSeconds > maxAgeSeconds) {
|
|
82
|
+
return { ok: false, reason: "l402_token_expired" };
|
|
83
|
+
}
|
|
84
|
+
if (!normalizeString(paymentPayload.macaroon_id || paymentPayload.macaroon)) {
|
|
85
|
+
return { ok: false, reason: "l402_macaroon_missing" };
|
|
86
|
+
}
|
|
87
|
+
if (!normalizeString(paymentPayload.invoice_id || paymentPayload.invoice_reference)) {
|
|
88
|
+
return { ok: false, reason: "l402_invoice_reference_missing" };
|
|
89
|
+
}
|
|
90
|
+
return { ok: true, age_seconds: ageSeconds };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function verifyL402AccessToken(headers, quote) {
|
|
94
|
+
const config = loadL402FrontConfig();
|
|
95
|
+
if (!config.enabled) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
status: 503,
|
|
99
|
+
reason: "l402_not_enabled"
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (!config.shared_secret) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
status: 503,
|
|
106
|
+
reason: "l402_shared_secret_missing"
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const payment = parseL402Authorization(headers || {});
|
|
110
|
+
if (!payment || !payment.encoded || !payment.signature || !payment.macaroon) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
status: 402,
|
|
114
|
+
reason: "l402_headers_incomplete"
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const expectedSignature = crypto.createHmac("sha256", config.shared_secret).update(payment.encoded).digest("hex");
|
|
118
|
+
if (!safeEqualHex(expectedSignature, payment.signature)) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
status: 403,
|
|
122
|
+
reason: "l402_signature_invalid"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const payload = decodeBase64UrlJson(payment.encoded);
|
|
126
|
+
const validation = verifyL402PaymentPayload(payload, quote, payment.wallet_id, config.max_age_seconds);
|
|
127
|
+
if (!validation.ok) {
|
|
128
|
+
return {
|
|
129
|
+
ok: false,
|
|
130
|
+
status: 403,
|
|
131
|
+
reason: validation.reason
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
ok: true,
|
|
136
|
+
verification_mode: "l402_gateway_signed",
|
|
137
|
+
wallet_id: payment.wallet_id,
|
|
138
|
+
payment_payload: payload,
|
|
139
|
+
age_seconds: validation.age_seconds,
|
|
140
|
+
l402: {
|
|
141
|
+
macaroon: payment.macaroon,
|
|
142
|
+
invoice_id: payment.invoice_id || payload.invoice_id || null,
|
|
143
|
+
preimage_hash: payment.preimage_hash || payload.preimage_hash || null
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function buildL402SecuritySummary() {
|
|
149
|
+
const config = loadL402FrontConfig();
|
|
150
|
+
const blockers = [];
|
|
151
|
+
if (config.enabled && !config.shared_secret) blockers.push("l402_shared_secret_missing");
|
|
152
|
+
return {
|
|
153
|
+
ok: true,
|
|
154
|
+
category: "machine-commerce-l402-security-summary",
|
|
155
|
+
summary_version: "xytara-l402-security-summary-v1",
|
|
156
|
+
enabled: config.enabled,
|
|
157
|
+
shared_secret_configured: Boolean(config.shared_secret),
|
|
158
|
+
max_age_seconds: config.max_age_seconds,
|
|
159
|
+
auth_scheme: config.auth_scheme,
|
|
160
|
+
token_shape: config.token_shape,
|
|
161
|
+
ready: config.enabled && blockers.length === 0,
|
|
162
|
+
blockers,
|
|
163
|
+
linked_surfaces: {
|
|
164
|
+
l402_lane_ref: "/v1/l402",
|
|
165
|
+
commands_execute_ref: "/l402/commands/execute",
|
|
166
|
+
mcp_invoke_ref: "/l402/mcp/tools/invoke"
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function buildL402ChallengePayload(quote) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
payment_required: {
|
|
175
|
+
scheme: "l402",
|
|
176
|
+
quote,
|
|
177
|
+
operator_requirements: {
|
|
178
|
+
auth_scheme: "L402",
|
|
179
|
+
token_shape: "base64url_json_with_macaroon_and_hmac_signed_payload",
|
|
180
|
+
security_ref: "/v1/l402/security"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = {
|
|
187
|
+
buildL402ChallengePayload,
|
|
188
|
+
buildL402SecuritySummary,
|
|
189
|
+
loadL402FrontConfig,
|
|
190
|
+
parseL402Authorization,
|
|
191
|
+
verifyL402AccessToken
|
|
192
|
+
};
|