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.
- package/Dockerfile +2 -2
- package/docs/CONFIG.md +12 -0
- package/docs/README.md +3 -0
- package/docs/ops/HOSTED_BASELINE_R2.md +4 -2
- package/docs/ops/MINIMUM_PRODUCTION_TOPOLOGY.md +19 -7
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +8 -3
- package/package.json +4 -1
- package/packages/api-sdk/README.md +71 -0
- package/packages/api-sdk/src/client.js +1021 -0
- package/packages/api-sdk/src/express-middleware.js +163 -0
- package/packages/api-sdk/src/index.d.ts +1662 -0
- package/packages/api-sdk/src/index.js +10 -0
- package/packages/api-sdk/src/webhook-signature.js +182 -0
- package/packages/api-sdk/src/x402-autopay.js +210 -0
- package/scripts/ci/cli-pack-smoke.mjs +2 -0
- package/scripts/ci/run-public-onboarding-gate.mjs +136 -0
- package/scripts/setup/login.mjs +73 -2
- package/scripts/setup/onboard.mjs +173 -28
- package/scripts/setup/onboarding-failure-taxonomy.mjs +107 -0
- package/scripts/setup/onboarding-state-machine.mjs +102 -0
- package/services/magic-link/README.md +352 -0
- package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_criteria.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/acceptance/acceptance_evaluation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/evidence/evidence_index.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/metering/metering_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/settld.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/payload/invoice_bundle/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/settld.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_definition.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/sla/sla_evaluation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-bad/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_criteria.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/acceptance/acceptance_evaluation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/evidence/evidence_index.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/invoice/invoice_claim.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/metering/metering_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/attestation/bundle_head_attestation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/events.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/events/payload_material.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/events.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/events/payload_material.jsonl +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/global/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/policy.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/revocations.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/events.jsonl +0 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/events/payload_material.jsonl +0 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/governance/tenant/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/job/snapshot.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/keys/public_keys.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/manifest.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/payload/job_proof_bundle/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/pricing/pricing_matrix_signatures.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/settld.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/payload/invoice_bundle/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/settld.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/sla/sla_definition.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/sla/sla_evaluation.json +1 -0
- package/services/magic-link/assets/samples/closepack/known-good/verify/verification_report.json +1 -0
- package/services/magic-link/assets/samples/trust.json +11 -0
- package/services/magic-link/src/audit-log.js +24 -0
- package/services/magic-link/src/buyer-auth.js +251 -0
- package/services/magic-link/src/buyer-notifications.js +402 -0
- package/services/magic-link/src/buyer-users.js +129 -0
- package/services/magic-link/src/decision-otp.js +187 -0
- package/services/magic-link/src/decisions.js +92 -0
- package/services/magic-link/src/email-resend.js +89 -0
- package/services/magic-link/src/ingest-keys.js +137 -0
- package/services/magic-link/src/maintenance.js +95 -0
- package/services/magic-link/src/onboarding-email-sequence.js +331 -0
- package/services/magic-link/src/payment-triggers.js +733 -0
- package/services/magic-link/src/pdf.js +149 -0
- package/services/magic-link/src/policy.js +69 -0
- package/services/magic-link/src/redaction.js +6 -0
- package/services/magic-link/src/render-model.js +70 -0
- package/services/magic-link/src/retention-gc.js +158 -0
- package/services/magic-link/src/run-records.js +496 -0
- package/services/magic-link/src/s3.js +171 -0
- package/services/magic-link/src/server.js +15849 -0
- package/services/magic-link/src/settlement-decisions.js +84 -0
- package/services/magic-link/src/smtp.js +217 -0
- package/services/magic-link/src/storage-cli.js +88 -0
- package/services/magic-link/src/storage-format.js +59 -0
- package/services/magic-link/src/tenant-billing.js +115 -0
- package/services/magic-link/src/tenant-onboarding.js +467 -0
- package/services/magic-link/src/tenant-settings.js +1140 -0
- package/services/magic-link/src/usage.js +80 -0
- package/services/magic-link/src/verify-queue.js +179 -0
- package/services/magic-link/src/verify-worker.js +157 -0
- package/services/magic-link/src/webhook-retries.js +542 -0
- package/services/magic-link/src/webhooks.js +218 -0
- 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
|
+
|