settld 0.2.4 → 0.2.6

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.
Files changed (128) hide show
  1. package/Dockerfile +2 -2
  2. package/docs/CONFIG.md +12 -0
  3. package/docs/README.md +3 -0
  4. package/docs/ops/HOSTED_BASELINE_R2.md +4 -2
  5. package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +19 -7
  6. package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +8 -3
  7. package/package.json +4 -1
  8. package/packages/api-sdk/README.md +71 -0
  9. package/packages/api-sdk/src/client.js +1021 -0
  10. package/packages/api-sdk/src/express-middleware.js +163 -0
  11. package/packages/api-sdk/src/index.d.ts +1662 -0
  12. package/packages/api-sdk/src/index.js +10 -0
  13. package/packages/api-sdk/src/webhook-signature.js +182 -0
  14. package/packages/api-sdk/src/x402-autopay.js +210 -0
  15. package/scripts/ci/cli-pack-smoke.mjs +2 -0
  16. package/scripts/ci/run-public-onboarding-gate.mjs +136 -0
  17. package/scripts/setup/login.mjs +73 -2
  18. package/scripts/setup/onboard.mjs +173 -28
  19. package/scripts/setup/onboarding-failure-taxonomy.mjs +107 -0
  20. package/scripts/setup/onboarding-state-machine.mjs +102 -0
  21. package/services/magic-link/README.md +352 -0
  22. package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_criteria.json +1 -0
  23. package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_evaluation.json +1 -0
  24. package/services/magic-link/assets/samples/closepack/known-bad/attestation/bundle_head_attestation.json +1 -0
  25. package/services/magic-link/assets/samples/closepack/known-bad/evidence/evidence_index.json +1 -0
  26. package/services/magic-link/assets/samples/closepack/known-bad/governance/policy.json +1 -0
  27. package/services/magic-link/assets/samples/closepack/known-bad/governance/revocations.json +1 -0
  28. package/services/magic-link/assets/samples/closepack/known-bad/manifest.json +1 -0
  29. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
  30. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/policy.json +1 -0
  31. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/revocations.json +1 -0
  32. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
  33. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/manifest.json +1 -0
  34. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/metering/metering_report.json +1 -0
  35. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
  36. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
  37. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
  38. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
  39. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
  40. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
  41. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
  42. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
  43. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
  44. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
  45. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
  46. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
  47. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
  48. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
  49. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
  50. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
  51. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
  52. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
  53. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/settld.json +1 -0
  54. package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/verify/verification_report.json +1 -0
  55. package/services/magic-link/assets/samples/closepack/known-bad/settld.json +1 -0
  56. package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_definition.json +1 -0
  57. package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_evaluation.json +1 -0
  58. package/services/magic-link/assets/samples/closepack/known-bad/verify/verification_report.json +1 -0
  59. package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_criteria.json +1 -0
  60. package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_evaluation.json +1 -0
  61. package/services/magic-link/assets/samples/closepack/known-good/attestation/bundle_head_attestation.json +1 -0
  62. package/services/magic-link/assets/samples/closepack/known-good/evidence/evidence_index.json +1 -0
  63. package/services/magic-link/assets/samples/closepack/known-good/governance/policy.json +1 -0
  64. package/services/magic-link/assets/samples/closepack/known-good/governance/revocations.json +1 -0
  65. package/services/magic-link/assets/samples/closepack/known-good/manifest.json +1 -0
  66. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
  67. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/policy.json +1 -0
  68. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/revocations.json +1 -0
  69. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
  70. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/manifest.json +1 -0
  71. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/metering/metering_report.json +1 -0
  72. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
  73. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
  74. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
  75. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
  76. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
  77. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
  78. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
  79. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
  80. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
  81. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
  82. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
  83. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
  84. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
  85. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
  86. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
  87. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
  88. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
  89. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
  90. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/settld.json +1 -0
  91. package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/verify/verification_report.json +1 -0
  92. package/services/magic-link/assets/samples/closepack/known-good/settld.json +1 -0
  93. package/services/magic-link/assets/samples/closepack/known-good/sla/sla_definition.json +1 -0
  94. package/services/magic-link/assets/samples/closepack/known-good/sla/sla_evaluation.json +1 -0
  95. package/services/magic-link/assets/samples/closepack/known-good/verify/verification_report.json +1 -0
  96. package/services/magic-link/assets/samples/trust.json +11 -0
  97. package/services/magic-link/src/audit-log.js +24 -0
  98. package/services/magic-link/src/buyer-auth.js +251 -0
  99. package/services/magic-link/src/buyer-notifications.js +402 -0
  100. package/services/magic-link/src/buyer-users.js +129 -0
  101. package/services/magic-link/src/decision-otp.js +187 -0
  102. package/services/magic-link/src/decisions.js +92 -0
  103. package/services/magic-link/src/email-resend.js +89 -0
  104. package/services/magic-link/src/ingest-keys.js +137 -0
  105. package/services/magic-link/src/maintenance.js +95 -0
  106. package/services/magic-link/src/onboarding-email-sequence.js +331 -0
  107. package/services/magic-link/src/payment-triggers.js +733 -0
  108. package/services/magic-link/src/pdf.js +149 -0
  109. package/services/magic-link/src/policy.js +69 -0
  110. package/services/magic-link/src/redaction.js +6 -0
  111. package/services/magic-link/src/render-model.js +70 -0
  112. package/services/magic-link/src/retention-gc.js +158 -0
  113. package/services/magic-link/src/run-records.js +496 -0
  114. package/services/magic-link/src/s3.js +171 -0
  115. package/services/magic-link/src/server.js +15849 -0
  116. package/services/magic-link/src/settlement-decisions.js +84 -0
  117. package/services/magic-link/src/smtp.js +217 -0
  118. package/services/magic-link/src/storage-cli.js +88 -0
  119. package/services/magic-link/src/storage-format.js +59 -0
  120. package/services/magic-link/src/tenant-billing.js +115 -0
  121. package/services/magic-link/src/tenant-onboarding.js +467 -0
  122. package/services/magic-link/src/tenant-settings.js +1140 -0
  123. package/services/magic-link/src/usage.js +80 -0
  124. package/services/magic-link/src/verify-queue.js +179 -0
  125. package/services/magic-link/src/verify-worker.js +157 -0
  126. package/services/magic-link/src/webhook-retries.js +542 -0
  127. package/services/magic-link/src/webhooks.js +218 -0
  128. package/src/api/app.js +135 -1
@@ -0,0 +1,163 @@
1
+ import {
2
+ SettldWebhookNoMatchingSignatureError,
3
+ SettldWebhookSignatureHeaderError,
4
+ SettldWebhookTimestampToleranceError,
5
+ verifySettldWebhookSignature
6
+ } from "./webhook-signature.js";
7
+
8
+ function isBodyVerifiable(body) {
9
+ if (body === null || body === undefined) return false;
10
+ if (typeof body === "string") return true;
11
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(body)) return true;
12
+ if (body instanceof Uint8Array) return true;
13
+ if (body instanceof ArrayBuffer) return true;
14
+ return false;
15
+ }
16
+
17
+ function readHeader(req, headerName) {
18
+ const key = String(headerName || "").toLowerCase();
19
+ if (typeof req?.get === "function") {
20
+ return req.get(key) ?? req.get(headerName) ?? null;
21
+ }
22
+ const headers = req?.headers;
23
+ if (!headers || typeof headers !== "object") return null;
24
+ const direct = headers[key];
25
+ if (Array.isArray(direct)) return direct[0] ?? null;
26
+ if (typeof direct === "string") return direct;
27
+ const alt = headers[headerName];
28
+ if (Array.isArray(alt)) return alt[0] ?? null;
29
+ return typeof alt === "string" ? alt : null;
30
+ }
31
+
32
+ function resolveMiddlewareOptions(optionsOrTolerance) {
33
+ if (optionsOrTolerance === undefined || optionsOrTolerance === null) {
34
+ return {
35
+ toleranceSeconds: 300,
36
+ signatureHeaderName: "x-settld-signature",
37
+ timestampHeaderName: "x-settld-timestamp"
38
+ };
39
+ }
40
+ if (typeof optionsOrTolerance === "number") {
41
+ return {
42
+ toleranceSeconds: optionsOrTolerance,
43
+ signatureHeaderName: "x-settld-signature",
44
+ timestampHeaderName: "x-settld-timestamp"
45
+ };
46
+ }
47
+ if (typeof optionsOrTolerance !== "object" || Array.isArray(optionsOrTolerance)) {
48
+ throw new TypeError("options must be a number or plain object");
49
+ }
50
+ return {
51
+ toleranceSeconds:
52
+ optionsOrTolerance.toleranceSeconds === undefined || optionsOrTolerance.toleranceSeconds === null
53
+ ? 300
54
+ : Number(optionsOrTolerance.toleranceSeconds),
55
+ signatureHeaderName: optionsOrTolerance.signatureHeaderName || "x-settld-signature",
56
+ timestampHeaderName: optionsOrTolerance.timestampHeaderName || "x-settld-timestamp"
57
+ };
58
+ }
59
+
60
+ function writeErrorResponse(res, statusCode, code, message) {
61
+ if (typeof res?.status === "function") {
62
+ const withStatus = res.status(statusCode);
63
+ if (typeof withStatus?.json === "function") {
64
+ withStatus.json({
65
+ ok: false,
66
+ error: { code, message }
67
+ });
68
+ return;
69
+ }
70
+ if (typeof withStatus?.send === "function") {
71
+ withStatus.send(message);
72
+ return;
73
+ }
74
+ }
75
+ if (typeof res?.writeHead === "function") {
76
+ res.writeHead(statusCode, { "content-type": "application/json; charset=utf-8" });
77
+ if (typeof res?.end === "function") {
78
+ res.end(JSON.stringify({ ok: false, error: { code, message } }));
79
+ return;
80
+ }
81
+ }
82
+ if (typeof res?.end === "function") {
83
+ res.end(JSON.stringify({ ok: false, error: { code, message } }));
84
+ }
85
+ }
86
+
87
+ function classifyVerificationError(err) {
88
+ if (err instanceof SettldWebhookSignatureHeaderError) {
89
+ return {
90
+ status: 400,
91
+ code: err.code || "SETTLD_WEBHOOK_SIGNATURE_HEADER_INVALID",
92
+ message: err.message
93
+ };
94
+ }
95
+ if (err instanceof SettldWebhookTimestampToleranceError) {
96
+ return {
97
+ status: 401,
98
+ code: err.code || "SETTLD_WEBHOOK_TIMESTAMP_OUTSIDE_TOLERANCE",
99
+ message: err.message
100
+ };
101
+ }
102
+ if (err instanceof SettldWebhookNoMatchingSignatureError) {
103
+ return {
104
+ status: 401,
105
+ code: err.code || "SETTLD_WEBHOOK_SIGNATURE_NO_MATCH",
106
+ message: err.message
107
+ };
108
+ }
109
+ return {
110
+ status: 401,
111
+ code: "SETTLD_WEBHOOK_UNAUTHORIZED",
112
+ message: err?.message || "webhook signature verification failed"
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Express-style middleware for Settld webhook verification.
118
+ *
119
+ * IMPORTANT:
120
+ * - This requires the raw request body bytes.
121
+ * - Use `req.rawBody` (preferred) or `req.body` as Buffer/string/typed array.
122
+ */
123
+ export function verifySettldWebhook(secretOrResolver, optionsOrTolerance = 300) {
124
+ const options = resolveMiddlewareOptions(optionsOrTolerance);
125
+ const resolveSecret =
126
+ typeof secretOrResolver === "function" ? secretOrResolver : () => secretOrResolver;
127
+
128
+ return function settldWebhookMiddleware(req, res, next) {
129
+ Promise.resolve()
130
+ .then(() => resolveSecret(req))
131
+ .then((secret) => {
132
+ const rawBody = req?.rawBody ?? req?.body;
133
+ if (!isBodyVerifiable(rawBody)) {
134
+ writeErrorResponse(
135
+ res,
136
+ 400,
137
+ "SETTLD_WEBHOOK_RAW_BODY_REQUIRED",
138
+ "Settld Webhook Error: request body is already parsed or missing raw bytes. Configure body-parser to preserve the raw buffer."
139
+ );
140
+ return;
141
+ }
142
+
143
+ const signatureHeader = readHeader(req, options.signatureHeaderName);
144
+ const timestampHeader = readHeader(req, options.timestampHeaderName);
145
+ verifySettldWebhookSignature(rawBody, signatureHeader ?? "", secret, {
146
+ toleranceSeconds: options.toleranceSeconds,
147
+ timestamp: timestampHeader
148
+ });
149
+ if (typeof next === "function") next();
150
+ })
151
+ .catch((err) => {
152
+ if (err instanceof TypeError && /secret is required/.test(String(err.message))) {
153
+ if (typeof next === "function") {
154
+ next(err);
155
+ return;
156
+ }
157
+ }
158
+ const classified = classifyVerificationError(err);
159
+ writeErrorResponse(res, classified.status, classified.code, classified.message);
160
+ });
161
+ };
162
+ }
163
+