motebit 1.4.2 → 1.4.3
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/dist/index.js +807 -514
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -982,6 +982,7 @@ var init_audience = __esm({
|
|
|
982
982
|
"task:submit",
|
|
983
983
|
"admin:query",
|
|
984
984
|
"proposal",
|
|
985
|
+
"receipts:read",
|
|
985
986
|
"account:balance",
|
|
986
987
|
"account:deposit",
|
|
987
988
|
"account:withdraw",
|
|
@@ -8061,6 +8062,7 @@ async function* runTurnStreaming(deps, userMessage, options) {
|
|
|
8061
8062
|
let iteration = 0;
|
|
8062
8063
|
let toolCallsSucceeded = 0;
|
|
8063
8064
|
let toolCallsBlocked = 0;
|
|
8065
|
+
let toolCallsDenied = 0;
|
|
8064
8066
|
let toolCallsFailed = 0;
|
|
8065
8067
|
const toolResultsLog = [];
|
|
8066
8068
|
let lastToolName = "";
|
|
@@ -8154,6 +8156,7 @@ async function* runTurnStreaming(deps, userMessage, options) {
|
|
|
8154
8156
|
const decision = deps.policyGate.validate(toolDef, toolCall.args, turnCtx);
|
|
8155
8157
|
if (!decision.allowed) {
|
|
8156
8158
|
toolCallsBlocked++;
|
|
8159
|
+
toolCallsDenied++;
|
|
8157
8160
|
yield {
|
|
8158
8161
|
type: "tool_status",
|
|
8159
8162
|
name: toolCall.name,
|
|
@@ -8591,6 +8594,7 @@ ${correction}`;
|
|
|
8591
8594
|
iterations: iteration,
|
|
8592
8595
|
toolCallsSucceeded,
|
|
8593
8596
|
toolCallsBlocked,
|
|
8597
|
+
toolCallsDenied,
|
|
8594
8598
|
toolCallsFailed,
|
|
8595
8599
|
...turnCtx && turnCtx.costAccumulated > 0 ? { totalTokens: turnCtx.costAccumulated } : {}
|
|
8596
8600
|
}
|
|
@@ -8638,6 +8642,7 @@ var dist_exports2 = {};
|
|
|
8638
8642
|
__export(dist_exports2, {
|
|
8639
8643
|
ADJUDICATOR_VOTE_SUITE: () => ADJUDICATOR_VOTE_SUITE,
|
|
8640
8644
|
AGENT_SETTLEMENT_ANCHOR_SUITE: () => AGENT_SETTLEMENT_ANCHOR_SUITE,
|
|
8645
|
+
APPROVAL_DECISION_SUITE: () => APPROVAL_DECISION_SUITE,
|
|
8641
8646
|
BALANCE_WAIVER_SUITE: () => BALANCE_WAIVER_SUITE,
|
|
8642
8647
|
COLLABORATIVE_RECEIPT_SUITE: () => COLLABORATIVE_RECEIPT_SUITE,
|
|
8643
8648
|
COMPUTER_SESSION_RECEIPT_SUITE: () => COMPUTER_SESSION_RECEIPT_SUITE,
|
|
@@ -8709,6 +8714,7 @@ __export(dist_exports2, {
|
|
|
8709
8714
|
resolveTreeHashVersion: () => resolveTreeHashVersion,
|
|
8710
8715
|
sha256: () => sha2562,
|
|
8711
8716
|
signAdjudicatorVote: () => signAdjudicatorVote,
|
|
8717
|
+
signApprovalDecision: () => signApprovalDecision,
|
|
8712
8718
|
signBalanceWaiver: () => signBalanceWaiver,
|
|
8713
8719
|
signBySuite: () => signBySuite,
|
|
8714
8720
|
signCertAsDelegate: () => signCertAsDelegate,
|
|
@@ -8747,6 +8753,7 @@ __export(dist_exports2, {
|
|
|
8747
8753
|
verify: () => verify,
|
|
8748
8754
|
verifyAdjudicatorVote: () => verifyAdjudicatorVote,
|
|
8749
8755
|
verifyAgentSettlementAnchor: () => verifyAgentSettlementAnchor,
|
|
8756
|
+
verifyApprovalDecision: () => verifyApprovalDecision,
|
|
8750
8757
|
verifyBalanceWaiver: () => verifyBalanceWaiver,
|
|
8751
8758
|
verifyBySuite: () => verifyBySuite,
|
|
8752
8759
|
verifyCollaborativeReceipt: () => verifyCollaborativeReceipt,
|
|
@@ -11970,387 +11977,6 @@ async function verifyMerkleInclusion(leaf, index, siblings, layerSizes, expected
|
|
|
11970
11977
|
}
|
|
11971
11978
|
return toHex(current) === expectedRoot;
|
|
11972
11979
|
}
|
|
11973
|
-
function signatureWithoutValue(sig) {
|
|
11974
|
-
return { suite: sig.suite, public_key: sig.public_key };
|
|
11975
|
-
}
|
|
11976
|
-
function canonicalizeSkillManifestBytes(manifest, body) {
|
|
11977
|
-
const motebitForCanonical = manifest.motebit.signature ? {
|
|
11978
|
-
...manifest.motebit,
|
|
11979
|
-
signature: signatureWithoutValue(manifest.motebit.signature)
|
|
11980
|
-
} : manifest.motebit;
|
|
11981
|
-
const manifestForCanonical = { ...manifest, motebit: motebitForCanonical };
|
|
11982
|
-
const canonical = canonicalJson(manifestForCanonical);
|
|
11983
|
-
const manifestBytes = new TextEncoder().encode(canonical);
|
|
11984
|
-
const out = new Uint8Array(manifestBytes.length + 1 + body.length);
|
|
11985
|
-
out.set(manifestBytes, 0);
|
|
11986
|
-
out[manifestBytes.length] = 10;
|
|
11987
|
-
out.set(body, manifestBytes.length + 1);
|
|
11988
|
-
return out;
|
|
11989
|
-
}
|
|
11990
|
-
function canonicalizeSkillEnvelopeBytes(envelope) {
|
|
11991
|
-
const envelopeForCanonical = {
|
|
11992
|
-
...envelope,
|
|
11993
|
-
signature: signatureWithoutValue(envelope.signature)
|
|
11994
|
-
};
|
|
11995
|
-
const canonical = canonicalJson(envelopeForCanonical);
|
|
11996
|
-
return new TextEncoder().encode(canonical);
|
|
11997
|
-
}
|
|
11998
|
-
async function signSkillManifest(unsigned, privateKey, publicKey, body) {
|
|
11999
|
-
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12000
|
-
const unsignedWithSig = {
|
|
12001
|
-
...unsigned,
|
|
12002
|
-
motebit: {
|
|
12003
|
-
...unsigned.motebit,
|
|
12004
|
-
signature: {
|
|
12005
|
-
suite: SKILL_SIGNATURE_SUITE,
|
|
12006
|
-
public_key: publicKeyHex,
|
|
12007
|
-
value: ""
|
|
12008
|
-
// placeholder; stripped by canonicalize
|
|
12009
|
-
}
|
|
12010
|
-
}
|
|
12011
|
-
};
|
|
12012
|
-
const message2 = canonicalizeSkillManifestBytes(unsignedWithSig, body);
|
|
12013
|
-
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
|
|
12014
|
-
return {
|
|
12015
|
-
...unsigned,
|
|
12016
|
-
motebit: {
|
|
12017
|
-
...unsigned.motebit,
|
|
12018
|
-
signature: {
|
|
12019
|
-
suite: SKILL_SIGNATURE_SUITE,
|
|
12020
|
-
public_key: publicKeyHex,
|
|
12021
|
-
value: toBase64Url(sig)
|
|
12022
|
-
}
|
|
12023
|
-
}
|
|
12024
|
-
};
|
|
12025
|
-
}
|
|
12026
|
-
async function signSkillEnvelope(unsigned, privateKey, publicKey) {
|
|
12027
|
-
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12028
|
-
const unsignedWithSig = {
|
|
12029
|
-
...unsigned,
|
|
12030
|
-
signature: {
|
|
12031
|
-
suite: SKILL_SIGNATURE_SUITE,
|
|
12032
|
-
public_key: publicKeyHex,
|
|
12033
|
-
value: ""
|
|
12034
|
-
// placeholder; stripped by canonicalize
|
|
12035
|
-
}
|
|
12036
|
-
};
|
|
12037
|
-
const message2 = canonicalizeSkillEnvelopeBytes(unsignedWithSig);
|
|
12038
|
-
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
|
|
12039
|
-
return {
|
|
12040
|
-
...unsigned,
|
|
12041
|
-
signature: {
|
|
12042
|
-
suite: SKILL_SIGNATURE_SUITE,
|
|
12043
|
-
public_key: publicKeyHex,
|
|
12044
|
-
value: toBase64Url(sig)
|
|
12045
|
-
}
|
|
12046
|
-
};
|
|
12047
|
-
}
|
|
12048
|
-
async function verifySkillManifest(manifest, body, publicKey) {
|
|
12049
|
-
return (await verifySkillManifestDetailed(manifest, body, publicKey)).valid;
|
|
12050
|
-
}
|
|
12051
|
-
async function verifySkillManifestDetailed(manifest, body, publicKey) {
|
|
12052
|
-
const sig = manifest.motebit.signature;
|
|
12053
|
-
if (!sig) return { valid: false, reason: "no_signature" };
|
|
12054
|
-
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
12055
|
-
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12056
|
-
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
12057
|
-
return { valid: false, reason: "bad_public_key" };
|
|
12058
|
-
}
|
|
12059
|
-
let sigBytes;
|
|
12060
|
-
try {
|
|
12061
|
-
sigBytes = fromBase64Url(sig.value);
|
|
12062
|
-
} catch {
|
|
12063
|
-
return { valid: false, reason: "bad_signature_value" };
|
|
12064
|
-
}
|
|
12065
|
-
const message2 = canonicalizeSkillManifestBytes(manifest, body);
|
|
12066
|
-
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
|
|
12067
|
-
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
12068
|
-
}
|
|
12069
|
-
async function verifySkillEnvelope(envelope, publicKey) {
|
|
12070
|
-
return (await verifySkillEnvelopeDetailed(envelope, publicKey)).valid;
|
|
12071
|
-
}
|
|
12072
|
-
async function verifySkillEnvelopeDetailed(envelope, publicKey) {
|
|
12073
|
-
const sig = envelope.signature;
|
|
12074
|
-
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
12075
|
-
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12076
|
-
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
12077
|
-
return { valid: false, reason: "bad_public_key" };
|
|
12078
|
-
}
|
|
12079
|
-
let sigBytes;
|
|
12080
|
-
try {
|
|
12081
|
-
sigBytes = fromBase64Url(sig.value);
|
|
12082
|
-
} catch {
|
|
12083
|
-
return { valid: false, reason: "bad_signature_value" };
|
|
12084
|
-
}
|
|
12085
|
-
const message2 = canonicalizeSkillEnvelopeBytes(envelope);
|
|
12086
|
-
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
|
|
12087
|
-
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
12088
|
-
}
|
|
12089
|
-
function bytesToLowerHex(bytes) {
|
|
12090
|
-
let hex = "";
|
|
12091
|
-
for (const b3 of bytes) {
|
|
12092
|
-
hex += b3.toString(16).padStart(2, "0");
|
|
12093
|
-
}
|
|
12094
|
-
return hex;
|
|
12095
|
-
}
|
|
12096
|
-
function decodeSkillSignaturePublicKey(sig) {
|
|
12097
|
-
return hexToBytes5(sig.public_key);
|
|
12098
|
-
}
|
|
12099
|
-
function verifyHardwareAttestationClaim(claim, expectedIdentityPublicKeyHex, verifiers, deviceCheckContext) {
|
|
12100
|
-
const platform = claim.platform;
|
|
12101
|
-
const errors = [];
|
|
12102
|
-
switch (platform) {
|
|
12103
|
-
case "secure_enclave":
|
|
12104
|
-
return verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex);
|
|
12105
|
-
case "software":
|
|
12106
|
-
errors.push({
|
|
12107
|
-
message: "platform `software` is a no-hardware sentinel; no verification channel"
|
|
12108
|
-
});
|
|
12109
|
-
return { valid: false, platform: "software", errors };
|
|
12110
|
-
case "device_check":
|
|
12111
|
-
if (verifiers?.deviceCheck) {
|
|
12112
|
-
return dispatchInjected(
|
|
12113
|
-
platform,
|
|
12114
|
-
verifiers.deviceCheck(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
12115
|
-
);
|
|
12116
|
-
}
|
|
12117
|
-
errors.push({
|
|
12118
|
-
message: `platform \`${platform}\` verifier not wired \u2014 pass { deviceCheck: deviceCheckVerifier(...) } from @motebit/crypto-appattest to enable`
|
|
12119
|
-
});
|
|
12120
|
-
return { valid: false, platform, errors };
|
|
12121
|
-
case "tpm":
|
|
12122
|
-
if (verifiers?.tpm) {
|
|
12123
|
-
return dispatchInjected(
|
|
12124
|
-
platform,
|
|
12125
|
-
verifiers.tpm(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
12126
|
-
);
|
|
12127
|
-
}
|
|
12128
|
-
errors.push({
|
|
12129
|
-
message: `platform \`${platform}\` verifier not wired \u2014 pass { tpm: ... } via the verifiers parameter to enable`
|
|
12130
|
-
});
|
|
12131
|
-
return { valid: false, platform, errors };
|
|
12132
|
-
case "play_integrity":
|
|
12133
|
-
if (verifiers?.playIntegrity) {
|
|
12134
|
-
return dispatchInjected(
|
|
12135
|
-
platform,
|
|
12136
|
-
verifiers.playIntegrity(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
12137
|
-
);
|
|
12138
|
-
}
|
|
12139
|
-
errors.push({
|
|
12140
|
-
message: `platform \`${platform}\` verifier not wired \u2014 pass { playIntegrity: ... } via the verifiers parameter to enable`
|
|
12141
|
-
});
|
|
12142
|
-
return { valid: false, platform, errors };
|
|
12143
|
-
case "webauthn":
|
|
12144
|
-
if (verifiers?.webauthn) {
|
|
12145
|
-
return dispatchInjected(
|
|
12146
|
-
platform,
|
|
12147
|
-
verifiers.webauthn(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
12148
|
-
);
|
|
12149
|
-
}
|
|
12150
|
-
errors.push({
|
|
12151
|
-
message: `platform \`${platform}\` verifier not wired \u2014 pass { webauthn: webauthnVerifier(...) } from @motebit/crypto-webauthn to enable`
|
|
12152
|
-
});
|
|
12153
|
-
return { valid: false, platform, errors };
|
|
12154
|
-
case "android_keystore":
|
|
12155
|
-
if (verifiers?.androidKeystore) {
|
|
12156
|
-
return dispatchInjected(
|
|
12157
|
-
platform,
|
|
12158
|
-
verifiers.androidKeystore(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
12159
|
-
);
|
|
12160
|
-
}
|
|
12161
|
-
errors.push({
|
|
12162
|
-
message: `platform \`${platform}\` verifier not wired \u2014 pass { androidKeystore: androidKeystoreVerifier(...) } from @motebit/crypto-android-keystore to enable`
|
|
12163
|
-
});
|
|
12164
|
-
return { valid: false, platform, errors };
|
|
12165
|
-
default:
|
|
12166
|
-
errors.push({
|
|
12167
|
-
message: `unknown platform \`${claim.platform}\` \u2014 not in the declared enum`
|
|
12168
|
-
});
|
|
12169
|
-
return { valid: false, platform: null, errors };
|
|
12170
|
-
}
|
|
12171
|
-
}
|
|
12172
|
-
async function dispatchInjected(platform, result) {
|
|
12173
|
-
const awaited = await Promise.resolve(result);
|
|
12174
|
-
return {
|
|
12175
|
-
valid: awaited.valid,
|
|
12176
|
-
platform,
|
|
12177
|
-
errors: awaited.errors ?? []
|
|
12178
|
-
};
|
|
12179
|
-
}
|
|
12180
|
-
function verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex) {
|
|
12181
|
-
const errors = [];
|
|
12182
|
-
const platform = "secure_enclave";
|
|
12183
|
-
if (claim.key_exported === true) {
|
|
12184
|
-
}
|
|
12185
|
-
if (!claim.attestation_receipt) {
|
|
12186
|
-
errors.push({
|
|
12187
|
-
message: "secure_enclave claim missing `attestation_receipt`"
|
|
12188
|
-
});
|
|
12189
|
-
return { valid: false, platform, errors };
|
|
12190
|
-
}
|
|
12191
|
-
const parts = claim.attestation_receipt.split(".");
|
|
12192
|
-
if (parts.length !== 2) {
|
|
12193
|
-
errors.push({
|
|
12194
|
-
message: `attestation_receipt must be 2 base64url parts separated by '.'; got ${parts.length}`
|
|
12195
|
-
});
|
|
12196
|
-
return { valid: false, platform, errors };
|
|
12197
|
-
}
|
|
12198
|
-
const [bodyB64, sigB64] = parts;
|
|
12199
|
-
let bodyBytes;
|
|
12200
|
-
let sigBytes;
|
|
12201
|
-
try {
|
|
12202
|
-
bodyBytes = fromBase64Url(bodyB64);
|
|
12203
|
-
sigBytes = fromBase64Url(sigB64);
|
|
12204
|
-
} catch (err2) {
|
|
12205
|
-
errors.push({
|
|
12206
|
-
message: `base64url decode failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
12207
|
-
});
|
|
12208
|
-
return { valid: false, platform, errors };
|
|
12209
|
-
}
|
|
12210
|
-
let bodyJson;
|
|
12211
|
-
try {
|
|
12212
|
-
bodyJson = JSON.parse(new TextDecoder().decode(bodyBytes));
|
|
12213
|
-
} catch (err2) {
|
|
12214
|
-
errors.push({
|
|
12215
|
-
message: `body JSON parse failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
12216
|
-
});
|
|
12217
|
-
return { valid: false, platform, errors };
|
|
12218
|
-
}
|
|
12219
|
-
const bodyCheck = parseSecureEnclaveBody(bodyJson);
|
|
12220
|
-
if (bodyCheck.kind === "error") {
|
|
12221
|
-
errors.push({ message: bodyCheck.reason });
|
|
12222
|
-
return { valid: false, platform, errors };
|
|
12223
|
-
}
|
|
12224
|
-
const body = bodyCheck.body;
|
|
12225
|
-
if (body.version !== "1") {
|
|
12226
|
-
errors.push({
|
|
12227
|
-
message: `unsupported body version '${body.version}' (expected '1')`
|
|
12228
|
-
});
|
|
12229
|
-
return { valid: false, platform, errors };
|
|
12230
|
-
}
|
|
12231
|
-
if (body.algorithm !== "ecdsa-p256-sha256") {
|
|
12232
|
-
errors.push({
|
|
12233
|
-
message: `unsupported body algorithm '${body.algorithm}' (expected 'ecdsa-p256-sha256')`
|
|
12234
|
-
});
|
|
12235
|
-
return { valid: false, platform, errors };
|
|
12236
|
-
}
|
|
12237
|
-
let sigValid;
|
|
12238
|
-
try {
|
|
12239
|
-
sigValid = verifyP256EcdsaSha256(body.se_public_key, bodyBytes, sigBytes);
|
|
12240
|
-
} catch (err2) {
|
|
12241
|
-
errors.push({
|
|
12242
|
-
message: `p-256 verification crashed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
12243
|
-
});
|
|
12244
|
-
return { valid: false, platform, errors };
|
|
12245
|
-
}
|
|
12246
|
-
if (!sigValid) {
|
|
12247
|
-
errors.push({
|
|
12248
|
-
message: "p-256 signature does not verify against body + se_public_key"
|
|
12249
|
-
});
|
|
12250
|
-
return { valid: false, platform, errors };
|
|
12251
|
-
}
|
|
12252
|
-
if (body.identity_public_key.toLowerCase() !== expectedIdentityPublicKeyHex.toLowerCase()) {
|
|
12253
|
-
errors.push({
|
|
12254
|
-
message: `identity_public_key mismatch: body names ${body.identity_public_key.slice(0, 16)}\u2026, expected ${expectedIdentityPublicKeyHex.slice(0, 16)}\u2026`
|
|
12255
|
-
});
|
|
12256
|
-
return { valid: false, platform, errors };
|
|
12257
|
-
}
|
|
12258
|
-
return {
|
|
12259
|
-
valid: true,
|
|
12260
|
-
platform,
|
|
12261
|
-
se_public_key: body.se_public_key,
|
|
12262
|
-
attested_at: body.attested_at,
|
|
12263
|
-
errors: []
|
|
12264
|
-
};
|
|
12265
|
-
}
|
|
12266
|
-
function parseSecureEnclaveBody(raw) {
|
|
12267
|
-
if (raw === null || typeof raw !== "object") {
|
|
12268
|
-
return { kind: "error", reason: "body is not a JSON object" };
|
|
12269
|
-
}
|
|
12270
|
-
const r2 = raw;
|
|
12271
|
-
const fields = [
|
|
12272
|
-
"version",
|
|
12273
|
-
"algorithm",
|
|
12274
|
-
"motebit_id",
|
|
12275
|
-
"device_id",
|
|
12276
|
-
"identity_public_key",
|
|
12277
|
-
"se_public_key",
|
|
12278
|
-
"attested_at"
|
|
12279
|
-
];
|
|
12280
|
-
for (const f6 of fields) {
|
|
12281
|
-
if (!(f6 in r2)) {
|
|
12282
|
-
return { kind: "error", reason: `body missing required field '${f6}'` };
|
|
12283
|
-
}
|
|
12284
|
-
}
|
|
12285
|
-
if (typeof r2.version !== "string" || typeof r2.algorithm !== "string" || typeof r2.motebit_id !== "string" || typeof r2.device_id !== "string" || typeof r2.identity_public_key !== "string" || typeof r2.se_public_key !== "string" || typeof r2.attested_at !== "number") {
|
|
12286
|
-
return { kind: "error", reason: "body field types invalid" };
|
|
12287
|
-
}
|
|
12288
|
-
return {
|
|
12289
|
-
kind: "ok",
|
|
12290
|
-
body: {
|
|
12291
|
-
version: r2.version,
|
|
12292
|
-
algorithm: r2.algorithm,
|
|
12293
|
-
motebit_id: r2.motebit_id,
|
|
12294
|
-
device_id: r2.device_id,
|
|
12295
|
-
identity_public_key: r2.identity_public_key,
|
|
12296
|
-
se_public_key: r2.se_public_key,
|
|
12297
|
-
attested_at: r2.attested_at
|
|
12298
|
-
}
|
|
12299
|
-
};
|
|
12300
|
-
}
|
|
12301
|
-
function encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes) {
|
|
12302
|
-
return `${toBase64Url2(bodyBytes)}.${toBase64Url2(sigBytes)}`;
|
|
12303
|
-
}
|
|
12304
|
-
function canonicalSecureEnclaveBodyForTest(body) {
|
|
12305
|
-
const full = {
|
|
12306
|
-
version: "1",
|
|
12307
|
-
algorithm: "ecdsa-p256-sha256",
|
|
12308
|
-
...body
|
|
12309
|
-
};
|
|
12310
|
-
return new TextEncoder().encode(canonicalJson(full));
|
|
12311
|
-
}
|
|
12312
|
-
async function mintSecureEnclaveReceiptForTest(input) {
|
|
12313
|
-
const { p256: p2562 } = await Promise.resolve().then(() => (init_nist(), nist_exports));
|
|
12314
|
-
const privateKey = p2562.utils.randomSecretKey();
|
|
12315
|
-
const sePublicKey = p2562.getPublicKey(privateKey, true);
|
|
12316
|
-
const sePublicKeyHex = bytesToHexLocal(sePublicKey);
|
|
12317
|
-
const bodyBytes = canonicalSecureEnclaveBodyForTest({
|
|
12318
|
-
motebit_id: input.motebit_id,
|
|
12319
|
-
device_id: input.device_id,
|
|
12320
|
-
identity_public_key: input.identity_public_key,
|
|
12321
|
-
se_public_key: sePublicKeyHex,
|
|
12322
|
-
attested_at: input.attested_at
|
|
12323
|
-
});
|
|
12324
|
-
const digest = await sha256Local(bodyBytes);
|
|
12325
|
-
const sigBytes = p2562.sign(digest, privateKey, { prehash: false, format: "der" });
|
|
12326
|
-
const receipt = encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes);
|
|
12327
|
-
return {
|
|
12328
|
-
claim: {
|
|
12329
|
-
platform: "secure_enclave",
|
|
12330
|
-
key_exported: false,
|
|
12331
|
-
attestation_receipt: receipt
|
|
12332
|
-
},
|
|
12333
|
-
sePublicKeyHex
|
|
12334
|
-
};
|
|
12335
|
-
}
|
|
12336
|
-
function bytesToHexLocal(bytes) {
|
|
12337
|
-
let hex = "";
|
|
12338
|
-
for (let i3 = 0; i3 < bytes.length; i3++) {
|
|
12339
|
-
hex += bytes[i3].toString(16).padStart(2, "0");
|
|
12340
|
-
}
|
|
12341
|
-
return hex;
|
|
12342
|
-
}
|
|
12343
|
-
async function sha256Local(bytes) {
|
|
12344
|
-
const buf = await crypto.subtle.digest("SHA-256", bytes);
|
|
12345
|
-
return new Uint8Array(buf);
|
|
12346
|
-
}
|
|
12347
|
-
function toBase64Url2(bytes) {
|
|
12348
|
-
let binary = "";
|
|
12349
|
-
for (let i3 = 0; i3 < bytes.length; i3++) {
|
|
12350
|
-
binary += String.fromCharCode(bytes[i3]);
|
|
12351
|
-
}
|
|
12352
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
12353
|
-
}
|
|
12354
11980
|
function isReceiptDebugEnabled() {
|
|
12355
11981
|
const g3 = globalThis;
|
|
12356
11982
|
if (g3.__motebit_debug_receipt_bytes === true) return true;
|
|
@@ -12677,6 +12303,27 @@ async function verifyAdjudicatorVote(vote, peerPublicKey) {
|
|
|
12677
12303
|
return false;
|
|
12678
12304
|
}
|
|
12679
12305
|
}
|
|
12306
|
+
async function signApprovalDecision(decision, approverPrivateKey, publicKey) {
|
|
12307
|
+
const withKey = publicKey ? { ...decision, public_key: bytesToHex4(publicKey) } : decision;
|
|
12308
|
+
const body = { ...withKey, suite: APPROVAL_DECISION_SUITE };
|
|
12309
|
+
const canonical = canonicalJson(body);
|
|
12310
|
+
const message2 = new TextEncoder().encode(canonical);
|
|
12311
|
+
const sig = await signBySuite(APPROVAL_DECISION_SUITE, message2, approverPrivateKey);
|
|
12312
|
+
const signed = { ...body, signature: toBase64Url(sig) };
|
|
12313
|
+
return Object.freeze(signed);
|
|
12314
|
+
}
|
|
12315
|
+
async function verifyApprovalDecision(decision, approverPublicKey) {
|
|
12316
|
+
if (decision.suite !== APPROVAL_DECISION_SUITE) return false;
|
|
12317
|
+
const { signature, ...body } = decision;
|
|
12318
|
+
const canonical = canonicalJson(body);
|
|
12319
|
+
const message2 = new TextEncoder().encode(canonical);
|
|
12320
|
+
try {
|
|
12321
|
+
const sig = fromBase64Url(signature);
|
|
12322
|
+
return await verifyBySuite(decision.suite, message2, sig, approverPublicKey);
|
|
12323
|
+
} catch {
|
|
12324
|
+
return false;
|
|
12325
|
+
}
|
|
12326
|
+
}
|
|
12680
12327
|
async function signDisputeResolution(resolution, adjudicatorPrivateKey) {
|
|
12681
12328
|
const body = { ...resolution, suite: DISPUTE_RESOLUTION_SUITE };
|
|
12682
12329
|
const canonical = canonicalJson(body);
|
|
@@ -13227,6 +12874,387 @@ async function signCredentialBundle(bundle, privateKey) {
|
|
|
13227
12874
|
);
|
|
13228
12875
|
return { ...withHash, signature: toBase64Url(sig) };
|
|
13229
12876
|
}
|
|
12877
|
+
function signatureWithoutValue(sig) {
|
|
12878
|
+
return { suite: sig.suite, public_key: sig.public_key };
|
|
12879
|
+
}
|
|
12880
|
+
function canonicalizeSkillManifestBytes(manifest, body) {
|
|
12881
|
+
const motebitForCanonical = manifest.motebit.signature ? {
|
|
12882
|
+
...manifest.motebit,
|
|
12883
|
+
signature: signatureWithoutValue(manifest.motebit.signature)
|
|
12884
|
+
} : manifest.motebit;
|
|
12885
|
+
const manifestForCanonical = { ...manifest, motebit: motebitForCanonical };
|
|
12886
|
+
const canonical = canonicalJson(manifestForCanonical);
|
|
12887
|
+
const manifestBytes = new TextEncoder().encode(canonical);
|
|
12888
|
+
const out = new Uint8Array(manifestBytes.length + 1 + body.length);
|
|
12889
|
+
out.set(manifestBytes, 0);
|
|
12890
|
+
out[manifestBytes.length] = 10;
|
|
12891
|
+
out.set(body, manifestBytes.length + 1);
|
|
12892
|
+
return out;
|
|
12893
|
+
}
|
|
12894
|
+
function canonicalizeSkillEnvelopeBytes(envelope) {
|
|
12895
|
+
const envelopeForCanonical = {
|
|
12896
|
+
...envelope,
|
|
12897
|
+
signature: signatureWithoutValue(envelope.signature)
|
|
12898
|
+
};
|
|
12899
|
+
const canonical = canonicalJson(envelopeForCanonical);
|
|
12900
|
+
return new TextEncoder().encode(canonical);
|
|
12901
|
+
}
|
|
12902
|
+
async function signSkillManifest(unsigned, privateKey, publicKey, body) {
|
|
12903
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12904
|
+
const unsignedWithSig = {
|
|
12905
|
+
...unsigned,
|
|
12906
|
+
motebit: {
|
|
12907
|
+
...unsigned.motebit,
|
|
12908
|
+
signature: {
|
|
12909
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
12910
|
+
public_key: publicKeyHex,
|
|
12911
|
+
value: ""
|
|
12912
|
+
// placeholder; stripped by canonicalize
|
|
12913
|
+
}
|
|
12914
|
+
}
|
|
12915
|
+
};
|
|
12916
|
+
const message2 = canonicalizeSkillManifestBytes(unsignedWithSig, body);
|
|
12917
|
+
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
|
|
12918
|
+
return {
|
|
12919
|
+
...unsigned,
|
|
12920
|
+
motebit: {
|
|
12921
|
+
...unsigned.motebit,
|
|
12922
|
+
signature: {
|
|
12923
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
12924
|
+
public_key: publicKeyHex,
|
|
12925
|
+
value: toBase64Url(sig)
|
|
12926
|
+
}
|
|
12927
|
+
}
|
|
12928
|
+
};
|
|
12929
|
+
}
|
|
12930
|
+
async function signSkillEnvelope(unsigned, privateKey, publicKey) {
|
|
12931
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12932
|
+
const unsignedWithSig = {
|
|
12933
|
+
...unsigned,
|
|
12934
|
+
signature: {
|
|
12935
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
12936
|
+
public_key: publicKeyHex,
|
|
12937
|
+
value: ""
|
|
12938
|
+
// placeholder; stripped by canonicalize
|
|
12939
|
+
}
|
|
12940
|
+
};
|
|
12941
|
+
const message2 = canonicalizeSkillEnvelopeBytes(unsignedWithSig);
|
|
12942
|
+
const sig = await signBySuite(SKILL_SIGNATURE_SUITE, message2, privateKey);
|
|
12943
|
+
return {
|
|
12944
|
+
...unsigned,
|
|
12945
|
+
signature: {
|
|
12946
|
+
suite: SKILL_SIGNATURE_SUITE,
|
|
12947
|
+
public_key: publicKeyHex,
|
|
12948
|
+
value: toBase64Url(sig)
|
|
12949
|
+
}
|
|
12950
|
+
};
|
|
12951
|
+
}
|
|
12952
|
+
async function verifySkillManifest(manifest, body, publicKey) {
|
|
12953
|
+
return (await verifySkillManifestDetailed(manifest, body, publicKey)).valid;
|
|
12954
|
+
}
|
|
12955
|
+
async function verifySkillManifestDetailed(manifest, body, publicKey) {
|
|
12956
|
+
const sig = manifest.motebit.signature;
|
|
12957
|
+
if (!sig) return { valid: false, reason: "no_signature" };
|
|
12958
|
+
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
12959
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12960
|
+
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
12961
|
+
return { valid: false, reason: "bad_public_key" };
|
|
12962
|
+
}
|
|
12963
|
+
let sigBytes;
|
|
12964
|
+
try {
|
|
12965
|
+
sigBytes = fromBase64Url(sig.value);
|
|
12966
|
+
} catch {
|
|
12967
|
+
return { valid: false, reason: "bad_signature_value" };
|
|
12968
|
+
}
|
|
12969
|
+
const message2 = canonicalizeSkillManifestBytes(manifest, body);
|
|
12970
|
+
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
|
|
12971
|
+
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
12972
|
+
}
|
|
12973
|
+
async function verifySkillEnvelope(envelope, publicKey) {
|
|
12974
|
+
return (await verifySkillEnvelopeDetailed(envelope, publicKey)).valid;
|
|
12975
|
+
}
|
|
12976
|
+
async function verifySkillEnvelopeDetailed(envelope, publicKey) {
|
|
12977
|
+
const sig = envelope.signature;
|
|
12978
|
+
if (sig.suite !== SKILL_SIGNATURE_SUITE) return { valid: false, reason: "wrong_suite" };
|
|
12979
|
+
const publicKeyHex = bytesToLowerHex(publicKey);
|
|
12980
|
+
if (sig.public_key.toLowerCase() !== publicKeyHex) {
|
|
12981
|
+
return { valid: false, reason: "bad_public_key" };
|
|
12982
|
+
}
|
|
12983
|
+
let sigBytes;
|
|
12984
|
+
try {
|
|
12985
|
+
sigBytes = fromBase64Url(sig.value);
|
|
12986
|
+
} catch {
|
|
12987
|
+
return { valid: false, reason: "bad_signature_value" };
|
|
12988
|
+
}
|
|
12989
|
+
const message2 = canonicalizeSkillEnvelopeBytes(envelope);
|
|
12990
|
+
const valid = await verifyBySuite(SKILL_SIGNATURE_SUITE, message2, sigBytes, publicKey);
|
|
12991
|
+
return { valid, reason: valid ? "ok" : "ed25519_mismatch" };
|
|
12992
|
+
}
|
|
12993
|
+
function bytesToLowerHex(bytes) {
|
|
12994
|
+
let hex = "";
|
|
12995
|
+
for (const b3 of bytes) {
|
|
12996
|
+
hex += b3.toString(16).padStart(2, "0");
|
|
12997
|
+
}
|
|
12998
|
+
return hex;
|
|
12999
|
+
}
|
|
13000
|
+
function decodeSkillSignaturePublicKey(sig) {
|
|
13001
|
+
return hexToBytes5(sig.public_key);
|
|
13002
|
+
}
|
|
13003
|
+
function verifyHardwareAttestationClaim(claim, expectedIdentityPublicKeyHex, verifiers, deviceCheckContext) {
|
|
13004
|
+
const platform = claim.platform;
|
|
13005
|
+
const errors = [];
|
|
13006
|
+
switch (platform) {
|
|
13007
|
+
case "secure_enclave":
|
|
13008
|
+
return verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex);
|
|
13009
|
+
case "software":
|
|
13010
|
+
errors.push({
|
|
13011
|
+
message: "platform `software` is a no-hardware sentinel; no verification channel"
|
|
13012
|
+
});
|
|
13013
|
+
return { valid: false, platform: "software", errors };
|
|
13014
|
+
case "device_check":
|
|
13015
|
+
if (verifiers?.deviceCheck) {
|
|
13016
|
+
return dispatchInjected(
|
|
13017
|
+
platform,
|
|
13018
|
+
verifiers.deviceCheck(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
13019
|
+
);
|
|
13020
|
+
}
|
|
13021
|
+
errors.push({
|
|
13022
|
+
message: `platform \`${platform}\` verifier not wired \u2014 pass { deviceCheck: deviceCheckVerifier(...) } from @motebit/crypto-appattest to enable`
|
|
13023
|
+
});
|
|
13024
|
+
return { valid: false, platform, errors };
|
|
13025
|
+
case "tpm":
|
|
13026
|
+
if (verifiers?.tpm) {
|
|
13027
|
+
return dispatchInjected(
|
|
13028
|
+
platform,
|
|
13029
|
+
verifiers.tpm(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
13030
|
+
);
|
|
13031
|
+
}
|
|
13032
|
+
errors.push({
|
|
13033
|
+
message: `platform \`${platform}\` verifier not wired \u2014 pass { tpm: ... } via the verifiers parameter to enable`
|
|
13034
|
+
});
|
|
13035
|
+
return { valid: false, platform, errors };
|
|
13036
|
+
case "play_integrity":
|
|
13037
|
+
if (verifiers?.playIntegrity) {
|
|
13038
|
+
return dispatchInjected(
|
|
13039
|
+
platform,
|
|
13040
|
+
verifiers.playIntegrity(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
13041
|
+
);
|
|
13042
|
+
}
|
|
13043
|
+
errors.push({
|
|
13044
|
+
message: `platform \`${platform}\` verifier not wired \u2014 pass { playIntegrity: ... } via the verifiers parameter to enable`
|
|
13045
|
+
});
|
|
13046
|
+
return { valid: false, platform, errors };
|
|
13047
|
+
case "webauthn":
|
|
13048
|
+
if (verifiers?.webauthn) {
|
|
13049
|
+
return dispatchInjected(
|
|
13050
|
+
platform,
|
|
13051
|
+
verifiers.webauthn(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
13052
|
+
);
|
|
13053
|
+
}
|
|
13054
|
+
errors.push({
|
|
13055
|
+
message: `platform \`${platform}\` verifier not wired \u2014 pass { webauthn: webauthnVerifier(...) } from @motebit/crypto-webauthn to enable`
|
|
13056
|
+
});
|
|
13057
|
+
return { valid: false, platform, errors };
|
|
13058
|
+
case "android_keystore":
|
|
13059
|
+
if (verifiers?.androidKeystore) {
|
|
13060
|
+
return dispatchInjected(
|
|
13061
|
+
platform,
|
|
13062
|
+
verifiers.androidKeystore(claim, expectedIdentityPublicKeyHex, deviceCheckContext)
|
|
13063
|
+
);
|
|
13064
|
+
}
|
|
13065
|
+
errors.push({
|
|
13066
|
+
message: `platform \`${platform}\` verifier not wired \u2014 pass { androidKeystore: androidKeystoreVerifier(...) } from @motebit/crypto-android-keystore to enable`
|
|
13067
|
+
});
|
|
13068
|
+
return { valid: false, platform, errors };
|
|
13069
|
+
default:
|
|
13070
|
+
errors.push({
|
|
13071
|
+
message: `unknown platform \`${claim.platform}\` \u2014 not in the declared enum`
|
|
13072
|
+
});
|
|
13073
|
+
return { valid: false, platform: null, errors };
|
|
13074
|
+
}
|
|
13075
|
+
}
|
|
13076
|
+
async function dispatchInjected(platform, result) {
|
|
13077
|
+
const awaited = await Promise.resolve(result);
|
|
13078
|
+
return {
|
|
13079
|
+
valid: awaited.valid,
|
|
13080
|
+
platform,
|
|
13081
|
+
errors: awaited.errors ?? []
|
|
13082
|
+
};
|
|
13083
|
+
}
|
|
13084
|
+
function verifySecureEnclaveClaim(claim, expectedIdentityPublicKeyHex) {
|
|
13085
|
+
const errors = [];
|
|
13086
|
+
const platform = "secure_enclave";
|
|
13087
|
+
if (claim.key_exported === true) {
|
|
13088
|
+
}
|
|
13089
|
+
if (!claim.attestation_receipt) {
|
|
13090
|
+
errors.push({
|
|
13091
|
+
message: "secure_enclave claim missing `attestation_receipt`"
|
|
13092
|
+
});
|
|
13093
|
+
return { valid: false, platform, errors };
|
|
13094
|
+
}
|
|
13095
|
+
const parts = claim.attestation_receipt.split(".");
|
|
13096
|
+
if (parts.length !== 2) {
|
|
13097
|
+
errors.push({
|
|
13098
|
+
message: `attestation_receipt must be 2 base64url parts separated by '.'; got ${parts.length}`
|
|
13099
|
+
});
|
|
13100
|
+
return { valid: false, platform, errors };
|
|
13101
|
+
}
|
|
13102
|
+
const [bodyB64, sigB64] = parts;
|
|
13103
|
+
let bodyBytes;
|
|
13104
|
+
let sigBytes;
|
|
13105
|
+
try {
|
|
13106
|
+
bodyBytes = fromBase64Url(bodyB64);
|
|
13107
|
+
sigBytes = fromBase64Url(sigB64);
|
|
13108
|
+
} catch (err2) {
|
|
13109
|
+
errors.push({
|
|
13110
|
+
message: `base64url decode failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
13111
|
+
});
|
|
13112
|
+
return { valid: false, platform, errors };
|
|
13113
|
+
}
|
|
13114
|
+
let bodyJson;
|
|
13115
|
+
try {
|
|
13116
|
+
bodyJson = JSON.parse(new TextDecoder().decode(bodyBytes));
|
|
13117
|
+
} catch (err2) {
|
|
13118
|
+
errors.push({
|
|
13119
|
+
message: `body JSON parse failed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
13120
|
+
});
|
|
13121
|
+
return { valid: false, platform, errors };
|
|
13122
|
+
}
|
|
13123
|
+
const bodyCheck = parseSecureEnclaveBody(bodyJson);
|
|
13124
|
+
if (bodyCheck.kind === "error") {
|
|
13125
|
+
errors.push({ message: bodyCheck.reason });
|
|
13126
|
+
return { valid: false, platform, errors };
|
|
13127
|
+
}
|
|
13128
|
+
const body = bodyCheck.body;
|
|
13129
|
+
if (body.version !== "1") {
|
|
13130
|
+
errors.push({
|
|
13131
|
+
message: `unsupported body version '${body.version}' (expected '1')`
|
|
13132
|
+
});
|
|
13133
|
+
return { valid: false, platform, errors };
|
|
13134
|
+
}
|
|
13135
|
+
if (body.algorithm !== "ecdsa-p256-sha256") {
|
|
13136
|
+
errors.push({
|
|
13137
|
+
message: `unsupported body algorithm '${body.algorithm}' (expected 'ecdsa-p256-sha256')`
|
|
13138
|
+
});
|
|
13139
|
+
return { valid: false, platform, errors };
|
|
13140
|
+
}
|
|
13141
|
+
let sigValid;
|
|
13142
|
+
try {
|
|
13143
|
+
sigValid = verifyP256EcdsaSha256(body.se_public_key, bodyBytes, sigBytes);
|
|
13144
|
+
} catch (err2) {
|
|
13145
|
+
errors.push({
|
|
13146
|
+
message: `p-256 verification crashed: ${err2 instanceof Error ? err2.message : String(err2)}`
|
|
13147
|
+
});
|
|
13148
|
+
return { valid: false, platform, errors };
|
|
13149
|
+
}
|
|
13150
|
+
if (!sigValid) {
|
|
13151
|
+
errors.push({
|
|
13152
|
+
message: "p-256 signature does not verify against body + se_public_key"
|
|
13153
|
+
});
|
|
13154
|
+
return { valid: false, platform, errors };
|
|
13155
|
+
}
|
|
13156
|
+
if (body.identity_public_key.toLowerCase() !== expectedIdentityPublicKeyHex.toLowerCase()) {
|
|
13157
|
+
errors.push({
|
|
13158
|
+
message: `identity_public_key mismatch: body names ${body.identity_public_key.slice(0, 16)}\u2026, expected ${expectedIdentityPublicKeyHex.slice(0, 16)}\u2026`
|
|
13159
|
+
});
|
|
13160
|
+
return { valid: false, platform, errors };
|
|
13161
|
+
}
|
|
13162
|
+
return {
|
|
13163
|
+
valid: true,
|
|
13164
|
+
platform,
|
|
13165
|
+
se_public_key: body.se_public_key,
|
|
13166
|
+
attested_at: body.attested_at,
|
|
13167
|
+
errors: []
|
|
13168
|
+
};
|
|
13169
|
+
}
|
|
13170
|
+
function parseSecureEnclaveBody(raw) {
|
|
13171
|
+
if (raw === null || typeof raw !== "object") {
|
|
13172
|
+
return { kind: "error", reason: "body is not a JSON object" };
|
|
13173
|
+
}
|
|
13174
|
+
const r2 = raw;
|
|
13175
|
+
const fields = [
|
|
13176
|
+
"version",
|
|
13177
|
+
"algorithm",
|
|
13178
|
+
"motebit_id",
|
|
13179
|
+
"device_id",
|
|
13180
|
+
"identity_public_key",
|
|
13181
|
+
"se_public_key",
|
|
13182
|
+
"attested_at"
|
|
13183
|
+
];
|
|
13184
|
+
for (const f6 of fields) {
|
|
13185
|
+
if (!(f6 in r2)) {
|
|
13186
|
+
return { kind: "error", reason: `body missing required field '${f6}'` };
|
|
13187
|
+
}
|
|
13188
|
+
}
|
|
13189
|
+
if (typeof r2.version !== "string" || typeof r2.algorithm !== "string" || typeof r2.motebit_id !== "string" || typeof r2.device_id !== "string" || typeof r2.identity_public_key !== "string" || typeof r2.se_public_key !== "string" || typeof r2.attested_at !== "number") {
|
|
13190
|
+
return { kind: "error", reason: "body field types invalid" };
|
|
13191
|
+
}
|
|
13192
|
+
return {
|
|
13193
|
+
kind: "ok",
|
|
13194
|
+
body: {
|
|
13195
|
+
version: r2.version,
|
|
13196
|
+
algorithm: r2.algorithm,
|
|
13197
|
+
motebit_id: r2.motebit_id,
|
|
13198
|
+
device_id: r2.device_id,
|
|
13199
|
+
identity_public_key: r2.identity_public_key,
|
|
13200
|
+
se_public_key: r2.se_public_key,
|
|
13201
|
+
attested_at: r2.attested_at
|
|
13202
|
+
}
|
|
13203
|
+
};
|
|
13204
|
+
}
|
|
13205
|
+
function encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes) {
|
|
13206
|
+
return `${toBase64Url2(bodyBytes)}.${toBase64Url2(sigBytes)}`;
|
|
13207
|
+
}
|
|
13208
|
+
function canonicalSecureEnclaveBodyForTest(body) {
|
|
13209
|
+
const full = {
|
|
13210
|
+
version: "1",
|
|
13211
|
+
algorithm: "ecdsa-p256-sha256",
|
|
13212
|
+
...body
|
|
13213
|
+
};
|
|
13214
|
+
return new TextEncoder().encode(canonicalJson(full));
|
|
13215
|
+
}
|
|
13216
|
+
async function mintSecureEnclaveReceiptForTest(input) {
|
|
13217
|
+
const { p256: p2562 } = await Promise.resolve().then(() => (init_nist(), nist_exports));
|
|
13218
|
+
const privateKey = p2562.utils.randomSecretKey();
|
|
13219
|
+
const sePublicKey = p2562.getPublicKey(privateKey, true);
|
|
13220
|
+
const sePublicKeyHex = bytesToHexLocal(sePublicKey);
|
|
13221
|
+
const bodyBytes = canonicalSecureEnclaveBodyForTest({
|
|
13222
|
+
motebit_id: input.motebit_id,
|
|
13223
|
+
device_id: input.device_id,
|
|
13224
|
+
identity_public_key: input.identity_public_key,
|
|
13225
|
+
se_public_key: sePublicKeyHex,
|
|
13226
|
+
attested_at: input.attested_at
|
|
13227
|
+
});
|
|
13228
|
+
const digest = await sha256Local(bodyBytes);
|
|
13229
|
+
const sigBytes = p2562.sign(digest, privateKey, { prehash: false, format: "der" });
|
|
13230
|
+
const receipt = encodeSecureEnclaveReceiptForTest(bodyBytes, sigBytes);
|
|
13231
|
+
return {
|
|
13232
|
+
claim: {
|
|
13233
|
+
platform: "secure_enclave",
|
|
13234
|
+
key_exported: false,
|
|
13235
|
+
attestation_receipt: receipt
|
|
13236
|
+
},
|
|
13237
|
+
sePublicKeyHex
|
|
13238
|
+
};
|
|
13239
|
+
}
|
|
13240
|
+
function bytesToHexLocal(bytes) {
|
|
13241
|
+
let hex = "";
|
|
13242
|
+
for (let i3 = 0; i3 < bytes.length; i3++) {
|
|
13243
|
+
hex += bytes[i3].toString(16).padStart(2, "0");
|
|
13244
|
+
}
|
|
13245
|
+
return hex;
|
|
13246
|
+
}
|
|
13247
|
+
async function sha256Local(bytes) {
|
|
13248
|
+
const buf = await crypto.subtle.digest("SHA-256", bytes);
|
|
13249
|
+
return new Uint8Array(buf);
|
|
13250
|
+
}
|
|
13251
|
+
function toBase64Url2(bytes) {
|
|
13252
|
+
let binary = "";
|
|
13253
|
+
for (let i3 = 0; i3 < bytes.length; i3++) {
|
|
13254
|
+
binary += String.fromCharCode(bytes[i3]);
|
|
13255
|
+
}
|
|
13256
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
13257
|
+
}
|
|
13230
13258
|
function buildVerificationMethod(publicKey) {
|
|
13231
13259
|
const did = publicKeyToDidKey(publicKey);
|
|
13232
13260
|
const fragment = did.slice("did:key:".length);
|
|
@@ -14508,6 +14536,9 @@ function detectArtifactType(artifact) {
|
|
|
14508
14536
|
if ("credentialSubject" in obj && "issuer" in obj && "proof" in obj) {
|
|
14509
14537
|
return "credential";
|
|
14510
14538
|
}
|
|
14539
|
+
if ("invocation_id" in obj && "tool_name" in obj && "motebit_id" in obj && "signature" in obj) {
|
|
14540
|
+
return "tool-invocation";
|
|
14541
|
+
}
|
|
14511
14542
|
if ("task_id" in obj && "motebit_id" in obj && "signature" in obj && "prompt_hash" in obj) {
|
|
14512
14543
|
return "receipt";
|
|
14513
14544
|
}
|
|
@@ -14887,23 +14918,26 @@ async function verifyIdentityBindingAnchored(identity, signingKeyHex, atTimestam
|
|
|
14887
14918
|
async function verifyReceiptSignature(receipt, publicKey) {
|
|
14888
14919
|
const { signature, ...body } = receipt;
|
|
14889
14920
|
if (!signature || signature.trim() === "") {
|
|
14890
|
-
return { valid: false, error: "
|
|
14921
|
+
return { valid: false, error: "\xA711.2 violation: receipt signature is empty" };
|
|
14891
14922
|
}
|
|
14892
14923
|
let sig;
|
|
14893
14924
|
try {
|
|
14894
14925
|
sig = fromBase64Url2(signature);
|
|
14895
14926
|
} catch {
|
|
14896
|
-
return { valid: false, error: "
|
|
14927
|
+
return { valid: false, error: "\xA711.2 violation: receipt signature is not valid base64url" };
|
|
14897
14928
|
}
|
|
14898
14929
|
if (sig.length !== 64) {
|
|
14899
|
-
return {
|
|
14930
|
+
return {
|
|
14931
|
+
valid: false,
|
|
14932
|
+
error: `\xA711.2 violation: receipt signature must be 64 bytes, got ${sig.length}`
|
|
14933
|
+
};
|
|
14900
14934
|
}
|
|
14901
14935
|
const canonical = canonicalJson2(body);
|
|
14902
14936
|
const message2 = new TextEncoder().encode(canonical);
|
|
14903
14937
|
const valid = await verifyBySuite("motebit-jcs-ed25519-b64-v1", message2, sig, publicKey);
|
|
14904
14938
|
return { valid };
|
|
14905
14939
|
}
|
|
14906
|
-
async function verifyReceipt(receipt) {
|
|
14940
|
+
async function verifyReceipt(receipt, options) {
|
|
14907
14941
|
let publicKey = null;
|
|
14908
14942
|
let signerDid;
|
|
14909
14943
|
if (receipt.public_key) {
|
|
@@ -14924,26 +14958,43 @@ async function verifyReceipt(receipt) {
|
|
|
14924
14958
|
type: "receipt",
|
|
14925
14959
|
valid: false,
|
|
14926
14960
|
receipt,
|
|
14927
|
-
errors: [
|
|
14961
|
+
errors: [
|
|
14962
|
+
{
|
|
14963
|
+
message: "\xA711.3 violation: No embedded public_key \u2014 cannot verify without known keys"
|
|
14964
|
+
}
|
|
14965
|
+
],
|
|
14928
14966
|
...delegations2.length > 0 ? { delegations: delegations2 } : {}
|
|
14929
14967
|
};
|
|
14930
14968
|
}
|
|
14931
14969
|
const sigResult = await verifyReceiptSignature(receipt, publicKey);
|
|
14932
14970
|
const errors = [];
|
|
14933
14971
|
if (!sigResult.valid) {
|
|
14934
|
-
errors.push({
|
|
14972
|
+
errors.push({
|
|
14973
|
+
message: sigResult.error ?? "\xA711.2 violation: Ed25519 signature did not verify"
|
|
14974
|
+
});
|
|
14935
14975
|
}
|
|
14936
14976
|
const delegations = await verifyReceiptDelegations(receipt);
|
|
14937
14977
|
const delegationErrors = delegations.filter((d4) => !d4.valid);
|
|
14938
14978
|
for (const d4 of delegationErrors) {
|
|
14939
14979
|
errors.push({
|
|
14940
|
-
message:
|
|
14980
|
+
message: `\xA711.5 violation: delegation ${d4.receipt?.task_id ?? "unknown"} verification failed`,
|
|
14941
14981
|
path: `delegation_receipts`
|
|
14942
14982
|
});
|
|
14943
14983
|
}
|
|
14984
|
+
let resultHashOk = true;
|
|
14985
|
+
if (options?.strictHashBinding) {
|
|
14986
|
+
const expected = await hash(new TextEncoder().encode(receipt.result));
|
|
14987
|
+
resultHashOk = expected === receipt.result_hash;
|
|
14988
|
+
if (!resultHashOk) {
|
|
14989
|
+
errors.push({
|
|
14990
|
+
message: "result_hash does not equal hex(SHA-256(result)) \u2014 receipt is not self-consistent (strict mode)",
|
|
14991
|
+
path: "result_hash"
|
|
14992
|
+
});
|
|
14993
|
+
}
|
|
14994
|
+
}
|
|
14944
14995
|
return {
|
|
14945
14996
|
type: "receipt",
|
|
14946
|
-
valid: sigResult.valid && delegationErrors.length === 0,
|
|
14997
|
+
valid: sigResult.valid && delegationErrors.length === 0 && resultHashOk,
|
|
14947
14998
|
receipt,
|
|
14948
14999
|
signer: signerDid,
|
|
14949
15000
|
keySource: "embedded",
|
|
@@ -14951,6 +15002,39 @@ async function verifyReceipt(receipt) {
|
|
|
14951
15002
|
...errors.length > 0 ? { errors } : {}
|
|
14952
15003
|
};
|
|
14953
15004
|
}
|
|
15005
|
+
async function verifyToolInvocation(receipt) {
|
|
15006
|
+
let publicKey = null;
|
|
15007
|
+
let signerDid;
|
|
15008
|
+
if (receipt.public_key) {
|
|
15009
|
+
try {
|
|
15010
|
+
publicKey = hexToBytes6(receipt.public_key);
|
|
15011
|
+
if (publicKey.length === 32) {
|
|
15012
|
+
signerDid = publicKeyToDidKey2(publicKey);
|
|
15013
|
+
} else {
|
|
15014
|
+
publicKey = null;
|
|
15015
|
+
}
|
|
15016
|
+
} catch {
|
|
15017
|
+
publicKey = null;
|
|
15018
|
+
}
|
|
15019
|
+
}
|
|
15020
|
+
if (!publicKey) {
|
|
15021
|
+
return {
|
|
15022
|
+
type: "tool-invocation",
|
|
15023
|
+
valid: false,
|
|
15024
|
+
toolInvocation: receipt,
|
|
15025
|
+
errors: [{ message: "No embedded public_key \u2014 cannot verify without known keys" }]
|
|
15026
|
+
};
|
|
15027
|
+
}
|
|
15028
|
+
const valid = await verifyToolInvocationReceipt(receipt, publicKey);
|
|
15029
|
+
return {
|
|
15030
|
+
type: "tool-invocation",
|
|
15031
|
+
valid,
|
|
15032
|
+
toolInvocation: receipt,
|
|
15033
|
+
signer: signerDid,
|
|
15034
|
+
keySource: "embedded",
|
|
15035
|
+
...valid ? {} : { errors: [{ message: "Ed25519 signature did not verify" }] }
|
|
15036
|
+
};
|
|
15037
|
+
}
|
|
14954
15038
|
async function verifyReceiptDelegations(receipt) {
|
|
14955
15039
|
if (!receipt.delegation_receipts || receipt.delegation_receipts.length === 0) {
|
|
14956
15040
|
return [];
|
|
@@ -15190,23 +15274,16 @@ async function verifyPresentation(vp, clockSkewSeconds = DEFAULT_CLOCK_SKEW_SECO
|
|
|
15190
15274
|
async function verify(artifact, options) {
|
|
15191
15275
|
const detected = detectArtifactType(artifact);
|
|
15192
15276
|
if (detected === null) {
|
|
15193
|
-
const
|
|
15277
|
+
const expected = options?.expectedType;
|
|
15194
15278
|
return {
|
|
15195
|
-
type:
|
|
15279
|
+
type: "unknown",
|
|
15196
15280
|
valid: false,
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
...fallbackType === "skill" ? {
|
|
15202
|
-
envelope: null,
|
|
15203
|
-
steps: {
|
|
15204
|
-
envelope: { valid: false, reason: "wrong_suite" },
|
|
15205
|
-
body_hash: null,
|
|
15206
|
-
files: []
|
|
15281
|
+
reason: "unrecognized_artifact_type",
|
|
15282
|
+
errors: [
|
|
15283
|
+
{
|
|
15284
|
+
message: expected ? `Unrecognized artifact format (expected "${expected}")` : "Unrecognized artifact format"
|
|
15207
15285
|
}
|
|
15208
|
-
|
|
15209
|
-
errors: [{ message: "Unrecognized artifact format" }]
|
|
15286
|
+
]
|
|
15210
15287
|
};
|
|
15211
15288
|
}
|
|
15212
15289
|
if (options?.expectedType && options.expectedType !== detected) {
|
|
@@ -15215,6 +15292,7 @@ async function verify(artifact, options) {
|
|
|
15215
15292
|
valid: false,
|
|
15216
15293
|
...detected === "identity" ? { identity: null } : {},
|
|
15217
15294
|
...detected === "receipt" ? { receipt: null } : {},
|
|
15295
|
+
...detected === "tool-invocation" ? { toolInvocation: null } : {},
|
|
15218
15296
|
...detected === "credential" ? { credential: null } : {},
|
|
15219
15297
|
...detected === "presentation" ? { presentation: null } : {},
|
|
15220
15298
|
...detected === "skill" ? {
|
|
@@ -15237,6 +15315,7 @@ async function verify(artifact, options) {
|
|
|
15237
15315
|
type: detected,
|
|
15238
15316
|
valid: false,
|
|
15239
15317
|
...detected === "receipt" ? { receipt: null } : {},
|
|
15318
|
+
...detected === "tool-invocation" ? { toolInvocation: null } : {},
|
|
15240
15319
|
...detected === "credential" ? { credential: null } : {},
|
|
15241
15320
|
...detected === "presentation" ? { presentation: null } : {},
|
|
15242
15321
|
...detected === "skill" ? {
|
|
@@ -15255,7 +15334,9 @@ async function verify(artifact, options) {
|
|
|
15255
15334
|
case "identity":
|
|
15256
15335
|
return verifyIdentity(resolved);
|
|
15257
15336
|
case "receipt":
|
|
15258
|
-
return verifyReceipt(resolved);
|
|
15337
|
+
return verifyReceipt(resolved, options);
|
|
15338
|
+
case "tool-invocation":
|
|
15339
|
+
return verifyToolInvocation(resolved);
|
|
15259
15340
|
case "credential":
|
|
15260
15341
|
return verifyCredential(
|
|
15261
15342
|
resolved,
|
|
@@ -15272,7 +15353,7 @@ async function verify(artifact, options) {
|
|
|
15272
15353
|
return verifySkillEnvelopeArtifact(resolved);
|
|
15273
15354
|
}
|
|
15274
15355
|
}
|
|
15275
|
-
var __defProp2, __getOwnPropNames2, __esm2, __export2, hasHexBuiltin, hexes, asciis, oidNist, init_utils, HashMD, SHA256_IV, SHA384_IV, SHA512_IV, init_md, U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H, init_u64, SHA256_K, SHA256_W, SHA2_32B, _SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA2_64B, _SHA512, _SHA384, sha256, sha512, sha384, init_sha2, abytes3, anumber2, bytesToHex3, concatBytes3, hexToBytes3, isBytes3, randomBytes3, _0n, _1n, isPosBig, bitMask, init_utils2, _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS, FIELD_SQRT, _Field, init_modular, _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF2, init_curve, init_fft, os2ip, _DST_scalar, init_hash_to_curve, validateSigners, validateCommitmentsNum, AggErr, init_frost, _DST_scalarBytes, init_oprf, _HMAC, hmac, init_hmac, divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2, init_weierstrass, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, p256_Point, p256, p256_hasher, p256_oprf, p256_FROST, p384_Point, p384, p384_hasher, p384_oprf, p521_Point, p521, p521_hasher, p521_oprf, init_nist, ed25519_CURVE, P, N, Gx, Gy, _a, _d, h, L, captureTrace, err, isBig, isStr, isBytes, abytes, u8n, u8fr, padh, bytesToHex, C, _ch, hexToBytes, cr, subtle, concatBytes, randomBytes, big, assertRange, M, P_MASK, modP, modN, invert, callHash, checkDigest, apoint, B256, Point, G, I, numTo32bLE, bytesToNumberLE, pow2, pow_2_252_3, RM1, uvRatio, modL_LE, sha512a, hash2extK, getExtendedPublicKeyAsync, getPublicKeyAsync, hashFinishA, _sign, signAsync, defaultVerifyOpts, _verify, verifyAsync, hashes, randomSecretKey, keygenAsync, W, scalarBits, pwindows, pwindowSize, precompute, Gpows, ctneg, wNAF, SIGNED_TOKEN_SUITE, BASE58_ALPHABET, NODE_TAG_V2, LEAF_TAG_V2,
|
|
15356
|
+
var __defProp2, __getOwnPropNames2, __esm2, __export2, hasHexBuiltin, hexes, asciis, oidNist, init_utils, HashMD, SHA256_IV, SHA384_IV, SHA512_IV, init_md, U32_MASK64, _32n, shrSH, shrSL, rotrSH, rotrSL, rotrBH, rotrBL, add3L, add3H, add4L, add4H, add5L, add5H, init_u64, SHA256_K, SHA256_W, SHA2_32B, _SHA256, K512, SHA512_Kh, SHA512_Kl, SHA512_W_H, SHA512_W_L, SHA2_64B, _SHA512, _SHA384, sha256, sha512, sha384, init_sha2, abytes3, anumber2, bytesToHex3, concatBytes3, hexToBytes3, isBytes3, randomBytes3, _0n, _1n, isPosBig, bitMask, init_utils2, _0n2, _1n2, _2n, _3n, _4n, _5n, _7n, _8n, _9n, _16n, FIELD_FIELDS, FIELD_SQRT, _Field, init_modular, _0n3, _1n3, pointPrecomputes, pointWindowSizes, wNAF2, init_curve, init_fft, os2ip, _DST_scalar, init_hash_to_curve, validateSigners, validateCommitmentsNum, AggErr, init_frost, _DST_scalarBytes, init_oprf, _HMAC, hmac, init_hmac, divNearest, DERErr, DER, _0n4, _1n4, _2n2, _3n2, _4n2, init_weierstrass, nist_exports, p256_CURVE, p384_CURVE, p521_CURVE, p256_Point, p256, p256_hasher, p256_oprf, p256_FROST, p384_Point, p384, p384_hasher, p384_oprf, p521_Point, p521, p521_hasher, p521_oprf, init_nist, ed25519_CURVE, P, N, Gx, Gy, _a, _d, h, L, captureTrace, err, isBig, isStr, isBytes, abytes, u8n, u8fr, padh, bytesToHex, C, _ch, hexToBytes, cr, subtle, concatBytes, randomBytes, big, assertRange, M, P_MASK, modP, modN, invert, callHash, checkDigest, apoint, B256, Point, G, I, numTo32bLE, bytesToNumberLE, pow2, pow_2_252_3, RM1, uvRatio, modL_LE, sha512a, hash2extK, getExtendedPublicKeyAsync, getPublicKeyAsync, hashFinishA, _sign, signAsync, defaultVerifyOpts, _verify, verifyAsync, hashes, randomSecretKey, keygenAsync, W, scalarBits, pwindows, pwindowSize, precompute, Gpows, ctneg, wNAF, SIGNED_TOKEN_SUITE, BASE58_ALPHABET, NODE_TAG_V2, LEAF_TAG_V2, EXECUTION_RECEIPT_SUITE, TOOL_INVOCATION_RECEIPT_SUITE, COMPUTER_SESSION_RECEIPT_SUITE, DELEGATION_TOKEN_SUITE, ADJUDICATOR_VOTE_SUITE, DISPUTE_RESOLUTION_SUITE, DISPUTE_REQUEST_SUITE, DISPUTE_EVIDENCE_SUITE, DISPUTE_APPEAL_SUITE, APPROVAL_DECISION_SUITE, CONSOLIDATION_RECEIPT_SUITE, BALANCE_WAIVER_SUITE, SETTLEMENT_RECORD_SUITE, FEDERATION_SETTLEMENT_RECORD_SUITE, KEY_SUCCESSION_SUITE, GUARDIAN_REVOCATION_SUITE, COLLABORATIVE_RECEIPT_SUITE, DEVICE_REGISTRATION_SUITE, DEVICE_REGISTRATION_MAX_AGE_MS, SKILL_SIGNATURE_SUITE, VC_TYPE_GRADIENT2, VC_TYPE_REPUTATION2, VC_TYPE_TRUST2, ONE_HOUR_MS, CONTENT_ARTIFACT_SUITE, CREDENTIAL_ANCHOR_SUITE, REVOCATION_ANCHOR_SUITE, AGENT_SETTLEMENT_ANCHOR_SUITE, FEDERATION_SETTLEMENT_ANCHOR_SUITE, DELETION_CERTIFICATE_SUITE, WITNESS_OMISSION_DISPUTE_WINDOW_MS, EMPTY_FEDERATION_GRAPH_ANCHOR_ROOT, REASON_TABLE, WITNESS_OMISSION_DISPUTE_SUITE, FEDERATION_HEARTBEAT_SUITE, HEARTBEAT_FRESHNESS_WINDOW_MS, BASE58_ALPHABET2, IDENTITY_FILE_SUITE, SIG_PREFIX, SIG_SUFFIX, DEFAULT_CLOCK_SKEW_SECONDS;
|
|
15276
15357
|
var init_dist5 = __esm({
|
|
15277
15358
|
"../../packages/crypto/dist/index.js"() {
|
|
15278
15359
|
"use strict";
|
|
@@ -17108,7 +17189,6 @@ var init_dist5 = __esm({
|
|
|
17108
17189
|
BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
17109
17190
|
NODE_TAG_V2 = new Uint8Array([1]);
|
|
17110
17191
|
LEAF_TAG_V2 = new Uint8Array([0]);
|
|
17111
|
-
SKILL_SIGNATURE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17112
17192
|
EXECUTION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17113
17193
|
TOOL_INVOCATION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17114
17194
|
COMPUTER_SESSION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
@@ -17118,6 +17198,7 @@ var init_dist5 = __esm({
|
|
|
17118
17198
|
DISPUTE_REQUEST_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17119
17199
|
DISPUTE_EVIDENCE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17120
17200
|
DISPUTE_APPEAL_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17201
|
+
APPROVAL_DECISION_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17121
17202
|
CONSOLIDATION_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17122
17203
|
BALANCE_WAIVER_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17123
17204
|
SETTLEMENT_RECORD_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
@@ -17127,6 +17208,7 @@ var init_dist5 = __esm({
|
|
|
17127
17208
|
COLLABORATIVE_RECEIPT_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17128
17209
|
DEVICE_REGISTRATION_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17129
17210
|
DEVICE_REGISTRATION_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
17211
|
+
SKILL_SIGNATURE_SUITE = "motebit-jcs-ed25519-b64-v1";
|
|
17130
17212
|
VC_TYPE_GRADIENT2 = "AgentGradientCredential";
|
|
17131
17213
|
VC_TYPE_REPUTATION2 = "AgentReputationCredential";
|
|
17132
17214
|
VC_TYPE_TRUST2 = "AgentTrustCredential";
|
|
@@ -18641,7 +18723,7 @@ var init_config2 = __esm({
|
|
|
18641
18723
|
"src/config.ts"() {
|
|
18642
18724
|
"use strict";
|
|
18643
18725
|
init_esm_shims();
|
|
18644
|
-
VERSION = true ? "1.4.
|
|
18726
|
+
VERSION = true ? "1.4.3" : "0.0.0-dev";
|
|
18645
18727
|
CONFIG_DIR = process.env["MOTEBIT_CONFIG_DIR"] ?? path2.join(os.homedir(), ".motebit");
|
|
18646
18728
|
CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
|
|
18647
18729
|
RELAY_DIR = path2.join(CONFIG_DIR, "relay");
|
|
@@ -40565,7 +40647,9 @@ var init_streaming = __esm({
|
|
|
40565
40647
|
args: chunk.args,
|
|
40566
40648
|
userMessage,
|
|
40567
40649
|
runId,
|
|
40568
|
-
quorum: chunk.quorum
|
|
40650
|
+
quorum: chunk.quorum,
|
|
40651
|
+
riskLevel: chunk.risk_level,
|
|
40652
|
+
requestedAt: Date.now()
|
|
40569
40653
|
};
|
|
40570
40654
|
const approvalStore = this.deps.getApprovalStore();
|
|
40571
40655
|
if (chunk.quorum && approvalStore) {
|
|
@@ -40617,6 +40701,7 @@ var init_streaming = __esm({
|
|
|
40617
40701
|
this.deps.pushStateUpdate({ processing: 0.9, attention: 0.8 });
|
|
40618
40702
|
this.deps.setSpeaking(true);
|
|
40619
40703
|
try {
|
|
40704
|
+
await this.signAndEmitApprovalDecision(pending, approved);
|
|
40620
40705
|
if (approved) {
|
|
40621
40706
|
yield { type: "tool_status", name: pending.toolName, status: "calling" };
|
|
40622
40707
|
const toolRegistry = this.deps.getToolRegistry();
|
|
@@ -40662,6 +40747,55 @@ var init_streaming = __esm({
|
|
|
40662
40747
|
this._isProcessing = false;
|
|
40663
40748
|
}
|
|
40664
40749
|
}
|
|
40750
|
+
/**
|
|
40751
|
+
* Build, sign, and emit the human-consent decision for a resolved approval.
|
|
40752
|
+
* Signed by the APPROVER's device key — consent is the approver's own
|
|
40753
|
+
* assertion, mirroring how the worker signs its own refusal. Commits to
|
|
40754
|
+
* `args_hash` (never raw args), the gated `approval_id` (= tool_call_id), the
|
|
40755
|
+
* verdict, and the requested/resolved timestamps, so a third party can verify
|
|
40756
|
+
* offline that this approver consented to THIS call at THIS time.
|
|
40757
|
+
*
|
|
40758
|
+
* Best-effort and fail-open on the AUDIT side only: if the signing material
|
|
40759
|
+
* is absent (no device key wired) or signing throws, the resume still
|
|
40760
|
+
* proceeds — the decision artifact is an audit record layered on top of the
|
|
40761
|
+
* already-enforced gate, not the gate itself. Mirrors `onToolInvocation`.
|
|
40762
|
+
*/
|
|
40763
|
+
async signAndEmitApprovalDecision(pending, approved) {
|
|
40764
|
+
const sink = this.deps.onApprovalDecision;
|
|
40765
|
+
if (!sink)
|
|
40766
|
+
return;
|
|
40767
|
+
const deviceId = this.deps.getDeviceId?.() ?? null;
|
|
40768
|
+
const privateKey = this.deps.getSigningPrivateKey?.() ?? null;
|
|
40769
|
+
const publicKey = this.deps.getSigningPublicKey?.() ?? null;
|
|
40770
|
+
if (!deviceId || !privateKey || !this.deps.motebitId)
|
|
40771
|
+
return;
|
|
40772
|
+
try {
|
|
40773
|
+
const argsHash = await hashToolPayload(pending.args);
|
|
40774
|
+
const unsigned = {
|
|
40775
|
+
approval_id: pending.toolCallId,
|
|
40776
|
+
motebit_id: this.deps.motebitId,
|
|
40777
|
+
device_id: deviceId,
|
|
40778
|
+
tool_name: pending.toolName,
|
|
40779
|
+
args_hash: argsHash,
|
|
40780
|
+
risk_level: pending.riskLevel ?? 0,
|
|
40781
|
+
verdict: approved ? "approved" : "denied",
|
|
40782
|
+
requested_at: pending.requestedAt,
|
|
40783
|
+
resolved_at: Date.now(),
|
|
40784
|
+
...approved ? {} : { denied_reason: "User denied this tool call." },
|
|
40785
|
+
...pending.runId != null ? { run_id: pending.runId } : {}
|
|
40786
|
+
};
|
|
40787
|
+
const signed = await signApprovalDecision(unsigned, privateKey, publicKey ?? void 0);
|
|
40788
|
+
try {
|
|
40789
|
+
sink(signed);
|
|
40790
|
+
} catch (err2) {
|
|
40791
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
40792
|
+
console.warn(`[streaming] onApprovalDecision sink threw: ${msg}`);
|
|
40793
|
+
}
|
|
40794
|
+
} catch (err2) {
|
|
40795
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
40796
|
+
console.warn(`[streaming] failed to sign approval decision: ${msg}`);
|
|
40797
|
+
}
|
|
40798
|
+
}
|
|
40665
40799
|
/**
|
|
40666
40800
|
* Record a single approval vote for multi-party (quorum) approval.
|
|
40667
40801
|
* - If no quorum is configured, delegates to resumeAfterApproval (backward compat).
|
|
@@ -42105,6 +42239,8 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
|
|
|
42105
42239
|
const toolsUsed = [];
|
|
42106
42240
|
let memoriesFormed = 0;
|
|
42107
42241
|
let status = "completed";
|
|
42242
|
+
let toolCallsSucceeded = 0;
|
|
42243
|
+
let toolCallsDenied = 0;
|
|
42108
42244
|
try {
|
|
42109
42245
|
const stream = deps.sendMessageStreaming(task.prompt, void 0, {
|
|
42110
42246
|
delegationScope: options?.delegatedScope
|
|
@@ -42124,6 +42260,8 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
|
|
|
42124
42260
|
} else if (chunk.type === "result") {
|
|
42125
42261
|
responseText = chunk.result.response;
|
|
42126
42262
|
memoriesFormed = chunk.result.memoriesFormed.length;
|
|
42263
|
+
toolCallsSucceeded = chunk.result.toolCallsSucceeded;
|
|
42264
|
+
toolCallsDenied = chunk.result.toolCallsDenied ?? 0;
|
|
42127
42265
|
}
|
|
42128
42266
|
yield chunk;
|
|
42129
42267
|
}
|
|
@@ -42134,6 +42272,10 @@ async function* handleAgentTask(deps, task, privateKey, deviceId, publicKey, opt
|
|
|
42134
42272
|
clearTimeout(timeout);
|
|
42135
42273
|
deps.restoreConversationContext(savedCtx);
|
|
42136
42274
|
}
|
|
42275
|
+
if (status === "completed" && toolCallsSucceeded === 0 && toolCallsDenied > 0) {
|
|
42276
|
+
status = "denied";
|
|
42277
|
+
responseText = `Task refused by governance: ${toolCallsDenied} action(s) exceeded this motebit's policy (deny_above / denylist / delegated scope) and no permitted action completed.` + (responseText ? ` Model note: ${responseText}` : "");
|
|
42278
|
+
}
|
|
42137
42279
|
const delegationReceipts = [];
|
|
42138
42280
|
for (const adapter of deps.getMcpAdapters()) {
|
|
42139
42281
|
if (adapter.getAndResetDelegationReceipts) {
|
|
@@ -42612,6 +42754,8 @@ var init_motebit_runtime = __esm({
|
|
|
42612
42754
|
* persist the payload beyond the call.
|
|
42613
42755
|
*/
|
|
42614
42756
|
_onToolActivity;
|
|
42757
|
+
/** Surface sink for signed human-consent decisions (the "approve" band artifact). */
|
|
42758
|
+
_onApprovalDecision;
|
|
42615
42759
|
/**
|
|
42616
42760
|
* receipts-1 — in-memory buffer of recent signed
|
|
42617
42761
|
* `ToolInvocationReceipt`s, capped at `RECEIPTS_BUFFER_CAP`.
|
|
@@ -42635,6 +42779,8 @@ var init_motebit_runtime = __esm({
|
|
|
42635
42779
|
*/
|
|
42636
42780
|
_recentReceipts = [];
|
|
42637
42781
|
static RECEIPTS_BUFFER_CAP = 50;
|
|
42782
|
+
/** In-session buffer of signed human-consent decisions (newest last), same cap + contract as `_recentReceipts`. */
|
|
42783
|
+
_recentApprovalDecisions = [];
|
|
42638
42784
|
constructor(config, adapters) {
|
|
42639
42785
|
assertSpeciesIntegrity();
|
|
42640
42786
|
this.motebitId = config.motebitId;
|
|
@@ -42656,6 +42802,7 @@ var init_motebit_runtime = __esm({
|
|
|
42656
42802
|
}
|
|
42657
42803
|
};
|
|
42658
42804
|
this._onToolActivity = config.onToolActivity ?? null;
|
|
42805
|
+
this._onApprovalDecision = config.onApprovalDecision ?? null;
|
|
42659
42806
|
this.compactionThreshold = config.compactionThreshold ?? 1e3;
|
|
42660
42807
|
this.mcpConfigs = config.mcpServers ?? [];
|
|
42661
42808
|
this.taskRouter = config.taskRouter ? new TaskRouter(config.taskRouter) : null;
|
|
@@ -42957,10 +43104,36 @@ var init_motebit_runtime = __esm({
|
|
|
42957
43104
|
getSigningPrivateKey: () => this._signingKeys?.privateKey ?? null,
|
|
42958
43105
|
getSigningPublicKey: () => this._signingKeys?.publicKey ?? null,
|
|
42959
43106
|
onToolInvocation: this._onToolInvocation ? (receipt) => this._onToolInvocation?.(receipt) : void 0,
|
|
42960
|
-
onToolActivity: this._onToolActivity ? (event) => this._onToolActivity?.(event) : void 0
|
|
43107
|
+
onToolActivity: this._onToolActivity ? (event) => this._onToolActivity?.(event) : void 0,
|
|
43108
|
+
onApprovalDecision: (decision) => this.recordApprovalDecision(decision)
|
|
42961
43109
|
});
|
|
42962
43110
|
this.wireLoopDeps();
|
|
42963
43111
|
}
|
|
43112
|
+
/**
|
|
43113
|
+
* Buffer a signed human-consent decision and forward it to the surface sink —
|
|
43114
|
+
* the exact shape as `_onToolInvocation`: push to the in-session buffer FIRST
|
|
43115
|
+
* (so `getRecentApprovalDecisions()` is correct regardless of whether a
|
|
43116
|
+
* surface listener throws), then fan out. Deliberately does NOT append an
|
|
43117
|
+
* event-log entry: the daemon/goal path already records its own
|
|
43118
|
+
* `ApprovalApproved`/`ApprovalDenied` goal-audit event (`apps/cli` scheduler),
|
|
43119
|
+
* so a runtime append would double-emit; and durable cross-restart archival of
|
|
43120
|
+
* the signed artifact is a separate, consumer-shaped concern (a dedicated
|
|
43121
|
+
* retrieval surface), deferred until a consumer forces its shape — the same
|
|
43122
|
+
* sequencing the delegation refusal path used for receipt retrieval.
|
|
43123
|
+
*/
|
|
43124
|
+
recordApprovalDecision(decision) {
|
|
43125
|
+
this._recentApprovalDecisions.push(decision);
|
|
43126
|
+
if (this._recentApprovalDecisions.length > _MotebitRuntime.RECEIPTS_BUFFER_CAP) {
|
|
43127
|
+
this._recentApprovalDecisions.shift();
|
|
43128
|
+
}
|
|
43129
|
+
if (this._onApprovalDecision) {
|
|
43130
|
+
try {
|
|
43131
|
+
this._onApprovalDecision(decision);
|
|
43132
|
+
} catch (err2) {
|
|
43133
|
+
this._logger.warn(`[runtime] surface onApprovalDecision threw: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
43134
|
+
}
|
|
43135
|
+
}
|
|
43136
|
+
}
|
|
42964
43137
|
// === Lifecycle ===
|
|
42965
43138
|
async init(target) {
|
|
42966
43139
|
this._sessionStartedAt = this._clock?.() ?? Date.now();
|
|
@@ -44854,6 +45027,18 @@ var init_motebit_runtime = __esm({
|
|
|
44854
45027
|
getRecentReceipts() {
|
|
44855
45028
|
return this._recentReceipts;
|
|
44856
45029
|
}
|
|
45030
|
+
/**
|
|
45031
|
+
* Recent signed human-consent decisions (`ApprovalDecision`), newest last,
|
|
45032
|
+
* capped at `RECEIPTS_BUFFER_CAP`. The "approve" governance band's
|
|
45033
|
+
* counterpart to `getRecentReceipts()`: each entry is the approver's signed,
|
|
45034
|
+
* offline-verifiable verdict over a gated tool call. Empty when no decisions
|
|
45035
|
+
* were rendered this session, or when no signing key was wired (fail-closed —
|
|
45036
|
+
* the runtime emits nothing rather than an unsigned decision). Durable
|
|
45037
|
+
* cross-restart archival is a deferred, consumer-shaped concern.
|
|
45038
|
+
*/
|
|
45039
|
+
getRecentApprovalDecisions() {
|
|
45040
|
+
return this._recentApprovalDecisions;
|
|
45041
|
+
}
|
|
44857
45042
|
/**
|
|
44858
45043
|
* Per-session pixel-passthrough consent. Composed into the loop's
|
|
44859
45044
|
* `projectForAi` pixel gate alongside provider mode and effective
|
|
@@ -74223,6 +74408,59 @@ var init_accounts = __esm({
|
|
|
74223
74408
|
}
|
|
74224
74409
|
});
|
|
74225
74410
|
|
|
74411
|
+
// ../../services/relay/dist/receipts-store.js
|
|
74412
|
+
function receiptTaskId(r2) {
|
|
74413
|
+
return r2.relay_task_id != null && r2.relay_task_id.length > 0 ? r2.relay_task_id : r2.task_id;
|
|
74414
|
+
}
|
|
74415
|
+
function persistReceiptChain(db, receipt, parentTaskId = null, depth = 0, receivedAt = Date.now()) {
|
|
74416
|
+
if (depth > MAX_RECEIPT_DEPTH) {
|
|
74417
|
+
logger13.warn("receipt.persist.depth_limit_exceeded", {
|
|
74418
|
+
motebitId: receipt.motebit_id,
|
|
74419
|
+
taskId: receiptTaskId(receipt),
|
|
74420
|
+
depth,
|
|
74421
|
+
maxDepth: MAX_RECEIPT_DEPTH
|
|
74422
|
+
});
|
|
74423
|
+
return;
|
|
74424
|
+
}
|
|
74425
|
+
const taskId = receiptTaskId(receipt);
|
|
74426
|
+
const receiptJson = canonicalJson(receipt);
|
|
74427
|
+
db.prepare(INSERT_SQL).run(receipt.motebit_id, taskId, parentTaskId, depth, receipt.status, receipt.suite, receipt.public_key ?? "", receipt.signature, receipt.invocation_origin ?? null, receiptJson, receivedAt);
|
|
74428
|
+
const children = receipt.delegation_receipts ?? [];
|
|
74429
|
+
for (const child of children) {
|
|
74430
|
+
persistReceiptChain(db, child, taskId, depth + 1, receivedAt);
|
|
74431
|
+
}
|
|
74432
|
+
}
|
|
74433
|
+
function getStoredReceiptJson(db, motebitId, taskId) {
|
|
74434
|
+
const row = db.prepare("SELECT receipt_json FROM relay_receipts WHERE motebit_id = ? AND task_id = ?").get(motebitId, taskId);
|
|
74435
|
+
return row?.receipt_json ?? null;
|
|
74436
|
+
}
|
|
74437
|
+
function listStoredReceipts(db, motebitId, limit = 50) {
|
|
74438
|
+
const capped = Math.max(1, Math.min(limit, 200));
|
|
74439
|
+
return db.prepare(`SELECT task_id, status, invocation_origin, received_at, receipt_json
|
|
74440
|
+
FROM relay_receipts
|
|
74441
|
+
WHERE motebit_id = ? AND depth = 0
|
|
74442
|
+
ORDER BY received_at DESC
|
|
74443
|
+
LIMIT ?`).all(motebitId, capped);
|
|
74444
|
+
}
|
|
74445
|
+
var logger13, MAX_RECEIPT_DEPTH, INSERT_SQL;
|
|
74446
|
+
var init_receipts_store = __esm({
|
|
74447
|
+
"../../services/relay/dist/receipts-store.js"() {
|
|
74448
|
+
"use strict";
|
|
74449
|
+
init_esm_shims();
|
|
74450
|
+
init_dist6();
|
|
74451
|
+
init_logger();
|
|
74452
|
+
logger13 = createLogger({ service: "receipts-store" });
|
|
74453
|
+
MAX_RECEIPT_DEPTH = 10;
|
|
74454
|
+
INSERT_SQL = `
|
|
74455
|
+
INSERT OR IGNORE INTO relay_receipts (
|
|
74456
|
+
motebit_id, task_id, parent_task_id, depth, status,
|
|
74457
|
+
suite, public_key, signature, invocation_origin,
|
|
74458
|
+
receipt_json, received_at
|
|
74459
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
74460
|
+
`;
|
|
74461
|
+
}
|
|
74462
|
+
});
|
|
74463
|
+
|
|
74226
74464
|
// ../../services/relay/dist/agent-revocation.js
|
|
74227
74465
|
async function buildSignedRevocationRecord(relayIdentity, input, now = Date.now()) {
|
|
74228
74466
|
const payload = {
|
|
@@ -74603,6 +74841,8 @@ function registerAgentRoutes(deps) {
|
|
|
74603
74841
|
agentAudience = "credentials";
|
|
74604
74842
|
} else if (path19.includes("/presentation")) {
|
|
74605
74843
|
agentAudience = "credentials:present";
|
|
74844
|
+
} else if (path19.includes("/receipts")) {
|
|
74845
|
+
agentAudience = "receipts:read";
|
|
74606
74846
|
} else {
|
|
74607
74847
|
agentAudience = "admin:query";
|
|
74608
74848
|
}
|
|
@@ -74613,6 +74853,27 @@ function registerAgentRoutes(deps) {
|
|
|
74613
74853
|
c3.set("callerMotebitId", claims.mid);
|
|
74614
74854
|
await next();
|
|
74615
74855
|
});
|
|
74856
|
+
app.get("/api/v1/agents/:motebitId/receipts", (c3) => {
|
|
74857
|
+
const motebitId = c3.req.param("motebitId");
|
|
74858
|
+
const caller = c3.get("callerMotebitId");
|
|
74859
|
+
if (caller !== void 0 && caller !== motebitId) {
|
|
74860
|
+
throw new HTTPException4(403, { message: "Cannot read another motebit's receipts" });
|
|
74861
|
+
}
|
|
74862
|
+
const limit = parseInt(c3.req.query("limit") ?? "50", 10) || 50;
|
|
74863
|
+
return c3.json({ receipts: listStoredReceipts(moteDb.db, motebitId, limit) });
|
|
74864
|
+
});
|
|
74865
|
+
app.get("/api/v1/agents/:motebitId/receipts/:taskId", (c3) => {
|
|
74866
|
+
const motebitId = c3.req.param("motebitId");
|
|
74867
|
+
const caller = c3.get("callerMotebitId");
|
|
74868
|
+
if (caller !== void 0 && caller !== motebitId) {
|
|
74869
|
+
throw new HTTPException4(403, { message: "Cannot read another motebit's receipts" });
|
|
74870
|
+
}
|
|
74871
|
+
const json = getStoredReceiptJson(moteDb.db, motebitId, c3.req.param("taskId"));
|
|
74872
|
+
if (json === null) {
|
|
74873
|
+
throw new HTTPException4(404, { message: "No such receipt" });
|
|
74874
|
+
}
|
|
74875
|
+
return c3.body(json, 200, { "content-type": "application/json" });
|
|
74876
|
+
});
|
|
74616
74877
|
app.post("/api/v1/agents/bootstrap", async (c3) => {
|
|
74617
74878
|
const body = await c3.req.json();
|
|
74618
74879
|
if (!body.motebit_id || typeof body.motebit_id !== "string" || body.motebit_id.trim() === "") {
|
|
@@ -74757,7 +75018,7 @@ function registerAgentRoutes(deps) {
|
|
|
74757
75018
|
}
|
|
74758
75019
|
moteDb.db.prepare(`INSERT INTO relay_key_successions (motebit_id, old_public_key, new_public_key, timestamp, reason, old_key_signature, new_key_signature, recovery, guardian_signature)
|
|
74759
75020
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(motebitId, succession.old_public_key, succession.new_public_key, succession.timestamp, succession.reason ?? null, succession.old_key_signature ?? null, succession.new_key_signature, succession.recovery ? 1 : 0, succession.guardian_signature ?? null);
|
|
74760
|
-
|
|
75021
|
+
logger14.info("agent.key.succession_on_register", {
|
|
74761
75022
|
motebitId,
|
|
74762
75023
|
oldKey: existingAgent.public_key.slice(0, 16) + "...",
|
|
74763
75024
|
newKey: publicKey.slice(0, 16) + "..."
|
|
@@ -75050,7 +75311,7 @@ function registerAgentRoutes(deps) {
|
|
|
75050
75311
|
note
|
|
75051
75312
|
});
|
|
75052
75313
|
insertRevocationRecord(moteDb.db, record);
|
|
75053
|
-
|
|
75314
|
+
logger14.info(revoked ? "agent.revoked" : "agent.reinstated", {
|
|
75054
75315
|
motebit_id: motebitId,
|
|
75055
75316
|
reason
|
|
75056
75317
|
});
|
|
@@ -75122,7 +75383,7 @@ function registerAgentRoutes(deps) {
|
|
|
75122
75383
|
return c3.json({ ok: true });
|
|
75123
75384
|
});
|
|
75124
75385
|
}
|
|
75125
|
-
var
|
|
75386
|
+
var logger14, SOLVENCY_TTL_MS;
|
|
75126
75387
|
var init_agents = __esm({
|
|
75127
75388
|
"../../services/relay/dist/agents.js"() {
|
|
75128
75389
|
"use strict";
|
|
@@ -75135,10 +75396,11 @@ var init_agents = __esm({
|
|
|
75135
75396
|
init_dist30();
|
|
75136
75397
|
init_idempotency();
|
|
75137
75398
|
init_accounts();
|
|
75399
|
+
init_receipts_store();
|
|
75138
75400
|
init_dist();
|
|
75139
75401
|
init_agent_revocation3();
|
|
75140
75402
|
init_logger();
|
|
75141
|
-
|
|
75403
|
+
logger14 = createLogger({ service: "agents" });
|
|
75142
75404
|
SOLVENCY_TTL_MS = 5 * 60 * 1e3;
|
|
75143
75405
|
}
|
|
75144
75406
|
});
|
|
@@ -75296,7 +75558,7 @@ async function insertRevocationEvent(db, relayIdentity, type, motebitId, opts) {
|
|
|
75296
75558
|
db.prepare("INSERT INTO relay_revocation_events (event_id, type, motebit_id, credential_id, new_public_key, timestamp, signature) VALUES (?, ?, ?, ?, ?, ?, ?)").run(eventId, type, motebitId, opts?.credentialId ?? null, opts?.newPublicKey ?? null, recordedAt, signatureHex);
|
|
75297
75559
|
if (revocationAnchorSubmitter && opts?.revokedPublicKey) {
|
|
75298
75560
|
anchorRevocationOnChain(opts.revokedPublicKey, effectiveAt).catch((err2) => {
|
|
75299
|
-
|
|
75561
|
+
logger15.error("revocation.anchor_failed", {
|
|
75300
75562
|
motebitId,
|
|
75301
75563
|
type,
|
|
75302
75564
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
@@ -75317,13 +75579,13 @@ async function anchorRevocationOnChain(revokedPublicKeyHex, timestamp) {
|
|
|
75317
75579
|
return;
|
|
75318
75580
|
const available = await revocationAnchorSubmitter.isAvailable();
|
|
75319
75581
|
if (!available) {
|
|
75320
|
-
|
|
75582
|
+
logger15.warn("revocation.anchor_submitter_unavailable", {
|
|
75321
75583
|
publicKey: revokedPublicKeyHex.slice(0, 16) + "..."
|
|
75322
75584
|
});
|
|
75323
75585
|
return;
|
|
75324
75586
|
}
|
|
75325
75587
|
const { txHash } = await revocationAnchorSubmitter.submitRevocation(revokedPublicKeyHex, timestamp);
|
|
75326
|
-
|
|
75588
|
+
logger15.info("revocation.anchored_onchain", {
|
|
75327
75589
|
publicKey: revokedPublicKeyHex.slice(0, 16) + "...",
|
|
75328
75590
|
txHash,
|
|
75329
75591
|
timestamp
|
|
@@ -75341,7 +75603,7 @@ async function processIncomingRevocations(db, events, peerPublicKey) {
|
|
|
75341
75603
|
const valid = await ed25519Verify(hexToBytes5(event.signature), encoder2.encode(payload), peerPublicKey);
|
|
75342
75604
|
if (!valid) {
|
|
75343
75605
|
rejected++;
|
|
75344
|
-
|
|
75606
|
+
logger15.warn("federation.revocation.invalid_signature", {
|
|
75345
75607
|
type: event.type,
|
|
75346
75608
|
motebitId: event.motebit_id
|
|
75347
75609
|
});
|
|
@@ -75519,13 +75781,13 @@ async function sendHeartbeats(db, relayIdentity) {
|
|
|
75519
75781
|
const newMissed = peer.missed_heartbeats + 1;
|
|
75520
75782
|
if (newMissed >= HEARTBEAT_REMOVE_THRESHOLD) {
|
|
75521
75783
|
db.prepare("UPDATE relay_peers SET missed_heartbeats = ?, state = 'removed' WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
|
|
75522
|
-
|
|
75784
|
+
logger15.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
|
|
75523
75785
|
} else if (newMissed >= HEARTBEAT_SUSPEND_THRESHOLD) {
|
|
75524
75786
|
db.prepare("UPDATE relay_peers SET missed_heartbeats = ?, state = 'suspended' WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
|
|
75525
|
-
|
|
75787
|
+
logger15.warn("federation.peer.suspended", { peerId: peer.peer_relay_id });
|
|
75526
75788
|
} else {
|
|
75527
75789
|
db.prepare("UPDATE relay_peers SET missed_heartbeats = ? WHERE peer_relay_id = ?").run(newMissed, peer.peer_relay_id);
|
|
75528
|
-
|
|
75790
|
+
logger15.warn("federation.heartbeat.missed", {
|
|
75529
75791
|
peerId: peer.peer_relay_id,
|
|
75530
75792
|
missed: newMissed
|
|
75531
75793
|
});
|
|
@@ -75569,7 +75831,7 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
|
|
|
75569
75831
|
try {
|
|
75570
75832
|
onRetryExhausted(retry);
|
|
75571
75833
|
} catch (refundErr) {
|
|
75572
|
-
|
|
75834
|
+
logger15.warn("settlement.retry.refund_failed", {
|
|
75573
75835
|
retryId: retry.retry_id,
|
|
75574
75836
|
taskId: retry.task_id,
|
|
75575
75837
|
error: refundErr instanceof Error ? refundErr.message : String(refundErr)
|
|
@@ -75579,7 +75841,7 @@ async function processSettlementRetries(db, relayIdentity, onRetryExhausted, ret
|
|
|
75579
75841
|
} else {
|
|
75580
75842
|
const backoffMs = nextRetryDelay(newAttempts - 1, retryPolicy);
|
|
75581
75843
|
const nextRetry = Date.now() + backoffMs;
|
|
75582
|
-
|
|
75844
|
+
logger15.info("settlement.retry.scheduled", {
|
|
75583
75845
|
retryId: retry.retry_id,
|
|
75584
75846
|
taskId: retry.task_id,
|
|
75585
75847
|
attempt: newAttempts,
|
|
@@ -75778,7 +76040,7 @@ function registerFederationRoutes(deps) {
|
|
|
75778
76040
|
}
|
|
75779
76041
|
const now = Date.now();
|
|
75780
76042
|
db.prepare(`UPDATE relay_peers SET state = 'active', peered_at = ?, last_heartbeat_at = ?, nonce = NULL WHERE peer_relay_id = ?`).run(now, now, relay_id);
|
|
75781
|
-
|
|
76043
|
+
logger15.info("federation.peer.active", { peerId: relay_id });
|
|
75782
76044
|
return c3.json({ status: "active", peered_at: now });
|
|
75783
76045
|
});
|
|
75784
76046
|
app.post("/federation/v1/peer/heartbeat", async (c3) => {
|
|
@@ -75812,19 +76074,19 @@ function registerFederationRoutes(deps) {
|
|
|
75812
76074
|
const peerPubKey = hexToBytes5(peer.public_key);
|
|
75813
76075
|
const result = await processIncomingRevocations(db, revocations, peerPubKey);
|
|
75814
76076
|
if (result.rejected > 0) {
|
|
75815
|
-
|
|
76077
|
+
logger15.warn("federation.revocation.rejected", {
|
|
75816
76078
|
peerId: relay_id,
|
|
75817
76079
|
rejected: result.rejected
|
|
75818
76080
|
});
|
|
75819
76081
|
}
|
|
75820
76082
|
if (result.processed > 0) {
|
|
75821
|
-
|
|
76083
|
+
logger15.info("federation.revocation.processed", {
|
|
75822
76084
|
peerId: relay_id,
|
|
75823
76085
|
processed: result.processed
|
|
75824
76086
|
});
|
|
75825
76087
|
}
|
|
75826
76088
|
} catch (err2) {
|
|
75827
|
-
|
|
76089
|
+
logger15.warn("federation.revocation.error", {
|
|
75828
76090
|
peerId: relay_id,
|
|
75829
76091
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
75830
76092
|
});
|
|
@@ -75871,7 +76133,7 @@ function registerFederationRoutes(deps) {
|
|
|
75871
76133
|
});
|
|
75872
76134
|
}
|
|
75873
76135
|
const witnessSignature = await signHorizonWitnessRequestBody(request.cert_body, relayIdentity.privateKey);
|
|
75874
|
-
|
|
76136
|
+
logger15.info("federation.horizon.witness.signed", {
|
|
75875
76137
|
issuerId: request.issuer_id,
|
|
75876
76138
|
storeId: request.cert_body.store_id,
|
|
75877
76139
|
horizonTs: request.cert_body.horizon_ts
|
|
@@ -75925,7 +76187,7 @@ function registerFederationRoutes(deps) {
|
|
|
75925
76187
|
});
|
|
75926
76188
|
}
|
|
75927
76189
|
persistWitnessOmissionDispute(db, dispute, disputeJson, "verified");
|
|
75928
|
-
|
|
76190
|
+
logger15.info("federation.horizon.dispute.verified", {
|
|
75929
76191
|
disputeId: dispute.dispute_id,
|
|
75930
76192
|
certIssuer: dispute.cert_issuer,
|
|
75931
76193
|
certSignature: dispute.cert_signature.slice(0, 16),
|
|
@@ -75968,7 +76230,7 @@ function registerFederationRoutes(deps) {
|
|
|
75968
76230
|
const canonical = canonicalJson(bodyForVerify);
|
|
75969
76231
|
const valid = await ed25519Verify(fromBase64Url(signature), new TextEncoder().encode(canonical), hexToBytes5(peer.public_key));
|
|
75970
76232
|
if (!valid) {
|
|
75971
|
-
|
|
76233
|
+
logger15.warn("federation.vote_request.signature_invalid", {
|
|
75972
76234
|
kind: "signature_invalid",
|
|
75973
76235
|
peerId: request.requester_id,
|
|
75974
76236
|
disputeId: request.dispute_id
|
|
@@ -76002,7 +76264,7 @@ function registerFederationRoutes(deps) {
|
|
|
76002
76264
|
vote: voteOutcome,
|
|
76003
76265
|
rationale: voteRationale
|
|
76004
76266
|
}, relayIdentity.privateKey);
|
|
76005
|
-
|
|
76267
|
+
logger15.info("federation.vote_request.signed", {
|
|
76006
76268
|
disputeId: request.dispute_id,
|
|
76007
76269
|
round: request.round,
|
|
76008
76270
|
requesterId: request.requester_id,
|
|
@@ -76208,7 +76470,7 @@ function registerFederationRoutes(deps) {
|
|
|
76208
76470
|
return c3.json(result);
|
|
76209
76471
|
});
|
|
76210
76472
|
}
|
|
76211
|
-
var FEDERATION_SUITE, RELAY_SPEC_VERSION,
|
|
76473
|
+
var FEDERATION_SUITE, RELAY_SPEC_VERSION, logger15, REVOCATION_TTL_MS, revocationAnchorSubmitter, PBKDF2_ITERATIONS, AUTH_TAG_BYTES, FEDERATION_QUERY_TTL_MS, HEARTBEAT_SUSPEND_THRESHOLD, HEARTBEAT_REMOVE_THRESHOLD, FEDERATION_TIMESTAMP_DRIFT_MS;
|
|
76212
76474
|
var init_federation = __esm({
|
|
76213
76475
|
"../../services/relay/dist/federation.js"() {
|
|
76214
76476
|
"use strict";
|
|
@@ -76227,7 +76489,7 @@ var init_federation = __esm({
|
|
|
76227
76489
|
init_dist30();
|
|
76228
76490
|
FEDERATION_SUITE = "motebit-concat-ed25519-hex-v1";
|
|
76229
76491
|
RELAY_SPEC_VERSION = "motebit/relay-federation@1.2";
|
|
76230
|
-
|
|
76492
|
+
logger15 = createLogger({ service: "relay", module: "federation" });
|
|
76231
76493
|
REVOCATION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
76232
76494
|
PBKDF2_ITERATIONS = 6e5;
|
|
76233
76495
|
AUTH_TAG_BYTES = 16;
|
|
@@ -76510,7 +76772,7 @@ function buildCreditOnDepositCallback(db) {
|
|
|
76510
76772
|
accountStore.credit(deposit.motebitId, microAmount, "deposit", `onchain-${deposit.txHash}-${deposit.logIndex}`, `USDC deposit from ${deposit.fromAddress} on ${deposit.chain} (block ${deposit.blockNumber})`);
|
|
76511
76773
|
db.prepare("INSERT INTO relay_deposit_log (tx_hash, log_index, agent_id, from_address, amount, chain, block_number, credited_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(deposit.txHash, deposit.logIndex, deposit.motebitId, deposit.fromAddress, deposit.amountOnchain.toString(), deposit.chain, deposit.blockNumber.toString(), Date.now());
|
|
76512
76774
|
});
|
|
76513
|
-
|
|
76775
|
+
logger16.info("deposit.detected", {
|
|
76514
76776
|
agentId: deposit.motebitId,
|
|
76515
76777
|
chain: deposit.chain,
|
|
76516
76778
|
amount: microAmount,
|
|
@@ -76534,7 +76796,7 @@ async function detectDeposits2(config) {
|
|
|
76534
76796
|
maxBlocksPerCycle: config.maxBlocksPerCycle,
|
|
76535
76797
|
confirmations,
|
|
76536
76798
|
onDeposit: buildCreditOnDepositCallback(config.db),
|
|
76537
|
-
logger:
|
|
76799
|
+
logger: logger16
|
|
76538
76800
|
});
|
|
76539
76801
|
}
|
|
76540
76802
|
function startDepositDetector(config) {
|
|
@@ -76545,7 +76807,7 @@ function startDepositDetector(config) {
|
|
|
76545
76807
|
const intervalMs = config.intervalMs ?? 15e3;
|
|
76546
76808
|
const maxBlocksPerCycle = config.maxBlocksPerCycle ?? 1e3;
|
|
76547
76809
|
if (!contractAddress || !config.rpc && !rpcUrl || confirmations === void 0) {
|
|
76548
|
-
|
|
76810
|
+
logger16.warn("deposit-detector.disabled", {
|
|
76549
76811
|
chain: config.chain,
|
|
76550
76812
|
reason: !contractAddress ? "no USDC contract" : !rpcUrl ? "no RPC URL" : "no confirmation depth registered (add to CONFIRMATIONS_BY_CHAIN)"
|
|
76551
76813
|
});
|
|
@@ -76556,7 +76818,7 @@ function startDepositDetector(config) {
|
|
|
76556
76818
|
const rpc = config.rpc ?? new HttpJsonRpcEvmAdapter({ rpcUrl, fetch: config.fetch });
|
|
76557
76819
|
const store = new SqliteDepositDetectorStore(config.db);
|
|
76558
76820
|
const onDeposit = buildCreditOnDepositCallback(config.db);
|
|
76559
|
-
|
|
76821
|
+
logger16.info("deposit-detector.started", {
|
|
76560
76822
|
chain: config.chain,
|
|
76561
76823
|
intervalMs,
|
|
76562
76824
|
maxBlocksPerCycle,
|
|
@@ -76573,16 +76835,16 @@ function startDepositDetector(config) {
|
|
|
76573
76835
|
maxBlocksPerCycle,
|
|
76574
76836
|
confirmations,
|
|
76575
76837
|
onDeposit,
|
|
76576
|
-
logger:
|
|
76838
|
+
logger: logger16
|
|
76577
76839
|
});
|
|
76578
76840
|
if (credits > 0) {
|
|
76579
|
-
|
|
76841
|
+
logger16.info("deposit-detector.cycle", {
|
|
76580
76842
|
chain: config.chain,
|
|
76581
76843
|
creditsApplied: credits
|
|
76582
76844
|
});
|
|
76583
76845
|
}
|
|
76584
76846
|
} catch (err2) {
|
|
76585
|
-
|
|
76847
|
+
logger16.warn("deposit-detector.cycle_failed", {
|
|
76586
76848
|
chain: config.chain,
|
|
76587
76849
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
76588
76850
|
});
|
|
@@ -76591,7 +76853,7 @@ function startDepositDetector(config) {
|
|
|
76591
76853
|
void tick();
|
|
76592
76854
|
return setInterval(() => void tick(), intervalMs);
|
|
76593
76855
|
}
|
|
76594
|
-
var
|
|
76856
|
+
var logger16, TRANSFER_TOPIC, USDC_CONTRACTS, DEFAULT_RPC_URLS, CONFIRMATIONS_BY_CHAIN, USDC_ONCHAIN_TO_MICRO, SqliteDepositDetectorStore;
|
|
76595
76857
|
var init_deposit_detector = __esm({
|
|
76596
76858
|
"../../services/relay/dist/deposit-detector.js"() {
|
|
76597
76859
|
"use strict";
|
|
@@ -76600,7 +76862,7 @@ var init_deposit_detector = __esm({
|
|
|
76600
76862
|
init_dist38();
|
|
76601
76863
|
init_account_store_sqlite();
|
|
76602
76864
|
init_logger();
|
|
76603
|
-
|
|
76865
|
+
logger16 = createLogger({ service: "deposit-detector" });
|
|
76604
76866
|
TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
76605
76867
|
USDC_CONTRACTS = {
|
|
76606
76868
|
"eip155:1": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
@@ -77644,13 +77906,13 @@ var stripe_webhook_adapter_exports = {};
|
|
|
77644
77906
|
__export(stripe_webhook_adapter_exports, {
|
|
77645
77907
|
StripeSubscriptionEventAdapter: () => StripeSubscriptionEventAdapter
|
|
77646
77908
|
});
|
|
77647
|
-
var
|
|
77909
|
+
var logger19, StripeSubscriptionEventAdapter;
|
|
77648
77910
|
var init_stripe_webhook_adapter = __esm({
|
|
77649
77911
|
"../../services/relay/dist/webhooks/stripe-webhook-adapter.js"() {
|
|
77650
77912
|
"use strict";
|
|
77651
77913
|
init_esm_shims();
|
|
77652
77914
|
init_logger();
|
|
77653
|
-
|
|
77915
|
+
logger19 = createLogger({ service: "relay", module: "stripe-webhook-adapter" });
|
|
77654
77916
|
StripeSubscriptionEventAdapter = class {
|
|
77655
77917
|
stripe;
|
|
77656
77918
|
webhookSecret;
|
|
@@ -77663,7 +77925,7 @@ var init_stripe_webhook_adapter = __esm({
|
|
|
77663
77925
|
try {
|
|
77664
77926
|
event = this.stripe.webhooks.constructEvent(rawBody, signature, this.webhookSecret);
|
|
77665
77927
|
} catch (err2) {
|
|
77666
|
-
|
|
77928
|
+
logger19.warn("webhook.signature_failed", {
|
|
77667
77929
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
77668
77930
|
});
|
|
77669
77931
|
return Promise.resolve(null);
|
|
@@ -92319,7 +92581,7 @@ async function reconcileTreasury(config) {
|
|
|
92319
92581
|
|
|
92320
92582
|
// ../../services/relay/dist/treasury-reconciliation.js
|
|
92321
92583
|
init_logger();
|
|
92322
|
-
var
|
|
92584
|
+
var logger17 = createLogger({ service: "treasury-reconciliation" });
|
|
92323
92585
|
var DEFAULT_INTERVAL_MS = 15 * 6e4;
|
|
92324
92586
|
var DEFAULT_CONFIRMATION_LAG_BUFFER_MS = 5 * 6e4;
|
|
92325
92587
|
var SqliteTreasuryReconciliationStore = class {
|
|
@@ -92400,11 +92662,11 @@ function startTreasuryReconciliationLoop(config) {
|
|
|
92400
92662
|
const generateReconciliationId = config.generateReconciliationId ?? randomUUID;
|
|
92401
92663
|
const store = new SqliteTreasuryReconciliationStore(config.db);
|
|
92402
92664
|
const loopLogger = {
|
|
92403
|
-
info: (event, data) =>
|
|
92404
|
-
warn: (event, data) =>
|
|
92405
|
-
error: (event, data) =>
|
|
92665
|
+
info: (event, data) => logger17.info(event, data),
|
|
92666
|
+
warn: (event, data) => logger17.warn(event, data),
|
|
92667
|
+
error: (event, data) => logger17.error(event, data)
|
|
92406
92668
|
};
|
|
92407
|
-
|
|
92669
|
+
logger17.info("treasury-reconciliation.started", {
|
|
92408
92670
|
chain: config.chain,
|
|
92409
92671
|
treasuryAddress: config.treasuryAddress,
|
|
92410
92672
|
usdcContractAddress: config.usdcContractAddress,
|
|
@@ -92427,7 +92689,7 @@ function startTreasuryReconciliationLoop(config) {
|
|
|
92427
92689
|
logger: loopLogger
|
|
92428
92690
|
});
|
|
92429
92691
|
} catch (err2) {
|
|
92430
|
-
|
|
92692
|
+
logger17.error("treasury-reconciliation.cycle_uncaught", {
|
|
92431
92693
|
chain: config.chain,
|
|
92432
92694
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
92433
92695
|
});
|
|
@@ -92443,7 +92705,7 @@ init_esm_shims();
|
|
|
92443
92705
|
init_dist39();
|
|
92444
92706
|
init_logger();
|
|
92445
92707
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
92446
|
-
var
|
|
92708
|
+
var logger18 = createLogger({ service: "solana-treasury-reconciliation" });
|
|
92447
92709
|
var DEFAULT_INTERVAL_MS2 = 15 * 6e4;
|
|
92448
92710
|
var SqliteSolanaTreasuryReconciliationStore = class {
|
|
92449
92711
|
db;
|
|
@@ -92489,11 +92751,11 @@ function startSolanaTreasuryReconciliationLoop(config) {
|
|
|
92489
92751
|
}), chain, usdcMint);
|
|
92490
92752
|
const store = new SqliteSolanaTreasuryReconciliationStore(config.db);
|
|
92491
92753
|
const loopLogger = {
|
|
92492
|
-
info: (event, data) =>
|
|
92493
|
-
warn: (event, data) =>
|
|
92494
|
-
error: (event, data) =>
|
|
92754
|
+
info: (event, data) => logger18.info(event, data),
|
|
92755
|
+
warn: (event, data) => logger18.warn(event, data),
|
|
92756
|
+
error: (event, data) => logger18.error(event, data)
|
|
92495
92757
|
};
|
|
92496
|
-
|
|
92758
|
+
logger18.info("solana-treasury-reconciliation.started", {
|
|
92497
92759
|
chain,
|
|
92498
92760
|
treasuryAddress: reconciler.treasuryAddress,
|
|
92499
92761
|
usdcMint,
|
|
@@ -92512,7 +92774,7 @@ function startSolanaTreasuryReconciliationLoop(config) {
|
|
|
92512
92774
|
logger: loopLogger
|
|
92513
92775
|
});
|
|
92514
92776
|
} catch (err2) {
|
|
92515
|
-
|
|
92777
|
+
logger18.error("solana-treasury-reconciliation.cycle_uncaught", {
|
|
92516
92778
|
chain,
|
|
92517
92779
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
92518
92780
|
});
|
|
@@ -92778,7 +93040,7 @@ init_dist6();
|
|
|
92778
93040
|
init_logger();
|
|
92779
93041
|
init_accounts();
|
|
92780
93042
|
import Stripe from "stripe";
|
|
92781
|
-
var
|
|
93043
|
+
var logger20 = createLogger({ service: "relay", module: "proxy-tokens" });
|
|
92782
93044
|
var DEPOSIT_MODELS = [
|
|
92783
93045
|
"claude-opus-4-6",
|
|
92784
93046
|
"claude-sonnet-4-6",
|
|
@@ -92839,7 +93101,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92839
93101
|
const balance = account?.balance ?? 0;
|
|
92840
93102
|
try {
|
|
92841
93103
|
const token = await issueProxyToken(motebitId, balance, relayIdentity);
|
|
92842
|
-
|
|
93104
|
+
logger20.info("proxy-token.issued", {
|
|
92843
93105
|
motebitId,
|
|
92844
93106
|
balance: fromMicro(balance)
|
|
92845
93107
|
});
|
|
@@ -92851,7 +93113,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92851
93113
|
expires_at: Date.now() + PROXY_TOKEN_TTL_MS
|
|
92852
93114
|
});
|
|
92853
93115
|
} catch (err2) {
|
|
92854
|
-
|
|
93116
|
+
logger20.error("proxy-token.failed", {
|
|
92855
93117
|
motebitId,
|
|
92856
93118
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
92857
93119
|
});
|
|
@@ -92871,13 +93133,13 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92871
93133
|
}
|
|
92872
93134
|
const newBalance = debitAccount(db, motebitId, body.amount, "fee", body.reference_id, body.description ?? "Cloud AI usage");
|
|
92873
93135
|
if (newBalance === null) {
|
|
92874
|
-
|
|
93136
|
+
logger20.warn("proxy-debit.insufficient", {
|
|
92875
93137
|
motebitId,
|
|
92876
93138
|
amount: body.amount
|
|
92877
93139
|
});
|
|
92878
93140
|
return c3.json({ success: false, balance: 0 });
|
|
92879
93141
|
}
|
|
92880
|
-
|
|
93142
|
+
logger20.info("proxy-debit.success", {
|
|
92881
93143
|
motebitId,
|
|
92882
93144
|
amount: body.amount,
|
|
92883
93145
|
balanceAfter: newBalance
|
|
@@ -92916,7 +93178,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92916
93178
|
creditAccount(db, motebitId, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud subscription \u2014 $${MONTHLY_CREDIT_USD} credits`);
|
|
92917
93179
|
}
|
|
92918
93180
|
const account = getAccountBalance(db, motebitId);
|
|
92919
|
-
|
|
93181
|
+
logger20.info("session-status.activated", { motebitId, subscriptionId });
|
|
92920
93182
|
return c3.json({
|
|
92921
93183
|
status: "complete",
|
|
92922
93184
|
motebit_id: motebitId,
|
|
@@ -92929,7 +93191,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92929
93191
|
return c3.json({ status: "expired" });
|
|
92930
93192
|
return c3.json({ status: "open" });
|
|
92931
93193
|
} catch (err2) {
|
|
92932
|
-
|
|
93194
|
+
logger20.error("session-status.failed", {
|
|
92933
93195
|
sessionId,
|
|
92934
93196
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
92935
93197
|
});
|
|
@@ -92943,7 +93205,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92943
93205
|
}
|
|
92944
93206
|
const priceId = process.env.STRIPE_CLOUD_PRICE_ID;
|
|
92945
93207
|
if (!priceId) {
|
|
92946
|
-
|
|
93208
|
+
logger20.error("checkout.missing_price_id", {});
|
|
92947
93209
|
return c3.json({ error: "subscription checkout not configured" }, 500);
|
|
92948
93210
|
}
|
|
92949
93211
|
const existing = db.prepare("SELECT status FROM relay_subscriptions WHERE motebit_id = ? AND status = 'active'").get(body.motebit_id);
|
|
@@ -92968,13 +93230,13 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
92968
93230
|
success_url: body.return_url ?? `${c3.req.url.split("/api")[0]}/?checkout_session_id={CHECKOUT_SESSION_ID}`,
|
|
92969
93231
|
cancel_url: body.return_url ?? `${c3.req.url.split("/api")[0]}/`
|
|
92970
93232
|
});
|
|
92971
|
-
|
|
93233
|
+
logger20.info("subscription.checkout.created", {
|
|
92972
93234
|
motebitId: body.motebit_id,
|
|
92973
93235
|
sessionId: session.id
|
|
92974
93236
|
});
|
|
92975
93237
|
return c3.json({ checkout_url: session.url, session_id: session.id });
|
|
92976
93238
|
} catch (err2) {
|
|
92977
|
-
|
|
93239
|
+
logger20.error("subscription.checkout.failed", {
|
|
92978
93240
|
motebitId: body.motebit_id,
|
|
92979
93241
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
92980
93242
|
});
|
|
@@ -93028,7 +93290,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
93028
93290
|
getOrCreateAccount(db, event.motebit_id);
|
|
93029
93291
|
creditAccount(db, event.motebit_id, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud subscription \u2014 $${MONTHLY_CREDIT_USD} credits`);
|
|
93030
93292
|
}
|
|
93031
|
-
|
|
93293
|
+
logger20.info("subscription.activated", {
|
|
93032
93294
|
motebitId: event.motebit_id,
|
|
93033
93295
|
subscriptionId: event.subscription_id,
|
|
93034
93296
|
creditUsd: MONTHLY_CREDIT_USD
|
|
@@ -93044,7 +93306,7 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
93044
93306
|
if (existingTxn != null)
|
|
93045
93307
|
break;
|
|
93046
93308
|
creditAccount(db, row.motebit_id, toMicro(MONTHLY_CREDIT_USD), "deposit", refId, `Motebit Cloud renewal \u2014 $${MONTHLY_CREDIT_USD} credits`);
|
|
93047
|
-
|
|
93309
|
+
logger20.info("subscription.renewed", {
|
|
93048
93310
|
motebitId: row.motebit_id,
|
|
93049
93311
|
subscriptionId: event.subscription_id,
|
|
93050
93312
|
invoiceId: event.invoice_id,
|
|
@@ -93057,19 +93319,19 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
93057
93319
|
if (!row)
|
|
93058
93320
|
break;
|
|
93059
93321
|
db.prepare("UPDATE relay_subscriptions SET status = 'cancelled', updated_at = ? WHERE stripe_subscription_id = ?").run(Date.now(), event.subscription_id);
|
|
93060
|
-
|
|
93322
|
+
logger20.info("subscription.cancelled", {
|
|
93061
93323
|
motebitId: row.motebit_id,
|
|
93062
93324
|
subscriptionId: event.subscription_id
|
|
93063
93325
|
});
|
|
93064
93326
|
break;
|
|
93065
93327
|
}
|
|
93066
93328
|
case "ignored": {
|
|
93067
|
-
|
|
93329
|
+
logger20.debug("webhook.unhandled", { type: event.type });
|
|
93068
93330
|
break;
|
|
93069
93331
|
}
|
|
93070
93332
|
}
|
|
93071
93333
|
} catch (err2) {
|
|
93072
|
-
|
|
93334
|
+
logger20.error("webhook.processing_failed", {
|
|
93073
93335
|
kind: event.kind,
|
|
93074
93336
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
93075
93337
|
});
|
|
@@ -93090,10 +93352,10 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
93090
93352
|
const periodEndSec = updated.current_period_end;
|
|
93091
93353
|
const periodEnd = typeof periodEndSec === "number" ? periodEndSec * 1e3 : null;
|
|
93092
93354
|
db.prepare("UPDATE relay_subscriptions SET status = 'cancelling', current_period_end = ?, updated_at = ? WHERE motebit_id = ?").run(periodEnd, Date.now(), motebitId);
|
|
93093
|
-
|
|
93355
|
+
logger20.info("subscription.cancel_scheduled", { motebitId, activeUntil: periodEnd });
|
|
93094
93356
|
return c3.json({ status: "cancelling", active_until: periodEnd });
|
|
93095
93357
|
} catch (err2) {
|
|
93096
|
-
|
|
93358
|
+
logger20.error("subscription.cancel_failed", {
|
|
93097
93359
|
motebitId,
|
|
93098
93360
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
93099
93361
|
});
|
|
@@ -93112,10 +93374,10 @@ function registerProxyTokenRoutes(app, db, relayIdentity, subscriptionEventAdapt
|
|
|
93112
93374
|
cancel_at_period_end: false
|
|
93113
93375
|
});
|
|
93114
93376
|
db.prepare("UPDATE relay_subscriptions SET status = 'active', updated_at = ? WHERE motebit_id = ?").run(Date.now(), motebitId);
|
|
93115
|
-
|
|
93377
|
+
logger20.info("subscription.resubscribed", { motebitId });
|
|
93116
93378
|
return c3.json({ status: "active" });
|
|
93117
93379
|
} catch (err2) {
|
|
93118
|
-
|
|
93380
|
+
logger20.error("subscription.resubscribe_failed", {
|
|
93119
93381
|
motebitId,
|
|
93120
93382
|
error: err2 instanceof Error ? err2.message : String(err2)
|
|
93121
93383
|
});
|
|
@@ -93558,44 +93820,8 @@ function registerReceiptExchangeRoutes(app, config = {}) {
|
|
|
93558
93820
|
};
|
|
93559
93821
|
}
|
|
93560
93822
|
|
|
93561
|
-
// ../../services/relay/dist/
|
|
93562
|
-
|
|
93563
|
-
init_dist6();
|
|
93564
|
-
init_logger();
|
|
93565
|
-
var logger20 = createLogger({ service: "receipts-store" });
|
|
93566
|
-
var MAX_RECEIPT_DEPTH = 10;
|
|
93567
|
-
var INSERT_SQL = `
|
|
93568
|
-
INSERT OR IGNORE INTO relay_receipts (
|
|
93569
|
-
motebit_id, task_id, parent_task_id, depth, status,
|
|
93570
|
-
suite, public_key, signature, invocation_origin,
|
|
93571
|
-
receipt_json, received_at
|
|
93572
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
93573
|
-
`;
|
|
93574
|
-
function receiptTaskId(r2) {
|
|
93575
|
-
return r2.relay_task_id != null && r2.relay_task_id.length > 0 ? r2.relay_task_id : r2.task_id;
|
|
93576
|
-
}
|
|
93577
|
-
function persistReceiptChain(db, receipt, parentTaskId = null, depth = 0, receivedAt = Date.now()) {
|
|
93578
|
-
if (depth > MAX_RECEIPT_DEPTH) {
|
|
93579
|
-
logger20.warn("receipt.persist.depth_limit_exceeded", {
|
|
93580
|
-
motebitId: receipt.motebit_id,
|
|
93581
|
-
taskId: receiptTaskId(receipt),
|
|
93582
|
-
depth,
|
|
93583
|
-
maxDepth: MAX_RECEIPT_DEPTH
|
|
93584
|
-
});
|
|
93585
|
-
return;
|
|
93586
|
-
}
|
|
93587
|
-
const taskId = receiptTaskId(receipt);
|
|
93588
|
-
const receiptJson = canonicalJson(receipt);
|
|
93589
|
-
db.prepare(INSERT_SQL).run(receipt.motebit_id, taskId, parentTaskId, depth, receipt.status, receipt.suite, receipt.public_key ?? "", receipt.signature, receipt.invocation_origin ?? null, receiptJson, receivedAt);
|
|
93590
|
-
const children = receipt.delegation_receipts ?? [];
|
|
93591
|
-
for (const child of children) {
|
|
93592
|
-
persistReceiptChain(db, child, taskId, depth + 1, receivedAt);
|
|
93593
|
-
}
|
|
93594
|
-
}
|
|
93595
|
-
function getStoredReceiptJson(db, motebitId, taskId) {
|
|
93596
|
-
const row = db.prepare("SELECT receipt_json FROM relay_receipts WHERE motebit_id = ? AND task_id = ?").get(motebitId, taskId);
|
|
93597
|
-
return row?.receipt_json ?? null;
|
|
93598
|
-
}
|
|
93823
|
+
// ../../services/relay/dist/index.js
|
|
93824
|
+
init_receipts_store();
|
|
93599
93825
|
|
|
93600
93826
|
// ../../services/relay/dist/onramp.js
|
|
93601
93827
|
init_esm_shims();
|
|
@@ -94121,6 +94347,7 @@ init_esm_shims();
|
|
|
94121
94347
|
init_dist2();
|
|
94122
94348
|
init_dist6();
|
|
94123
94349
|
init_dist5();
|
|
94350
|
+
init_receipts_store();
|
|
94124
94351
|
import { HTTPException as HTTPException10 } from "hono/http-exception";
|
|
94125
94352
|
var MOTEBIT_RELAY_CLAIM_GENERATOR = "motebit-relay/0.5.2";
|
|
94126
94353
|
function registerStateExportRoutes(deps) {
|
|
@@ -96508,6 +96735,68 @@ import { HTTPException as HTTPException18 } from "hono/http-exception";
|
|
|
96508
96735
|
init_esm_shims();
|
|
96509
96736
|
init_logger();
|
|
96510
96737
|
var logger27 = createLogger({ service: "push-adapter" });
|
|
96738
|
+
var EXPO_PUSH_URL = "https://exp.host/--/api/v2/push/send";
|
|
96739
|
+
var ExpoPushAdapter = class {
|
|
96740
|
+
accessToken;
|
|
96741
|
+
_fetch;
|
|
96742
|
+
constructor(config) {
|
|
96743
|
+
this.accessToken = config?.accessToken;
|
|
96744
|
+
this._fetch = config?.fetch ?? globalThis.fetch;
|
|
96745
|
+
}
|
|
96746
|
+
isAvailable() {
|
|
96747
|
+
return true;
|
|
96748
|
+
}
|
|
96749
|
+
async send(pushToken, _platform, payload) {
|
|
96750
|
+
const headers = {
|
|
96751
|
+
"Content-Type": "application/json"
|
|
96752
|
+
};
|
|
96753
|
+
if (this.accessToken) {
|
|
96754
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
96755
|
+
}
|
|
96756
|
+
const body = {
|
|
96757
|
+
to: pushToken,
|
|
96758
|
+
data: payload,
|
|
96759
|
+
priority: "high",
|
|
96760
|
+
channelId: "agent-tasks",
|
|
96761
|
+
_contentAvailable: true,
|
|
96762
|
+
sound: null
|
|
96763
|
+
};
|
|
96764
|
+
try {
|
|
96765
|
+
const res = await this._fetch(EXPO_PUSH_URL, {
|
|
96766
|
+
method: "POST",
|
|
96767
|
+
headers,
|
|
96768
|
+
body: JSON.stringify(body),
|
|
96769
|
+
signal: AbortSignal.timeout(1e4)
|
|
96770
|
+
});
|
|
96771
|
+
if (!res.ok) {
|
|
96772
|
+
const text = await res.text().catch(() => "");
|
|
96773
|
+
return { delivered: false, error: `HTTP ${res.status}: ${text}` };
|
|
96774
|
+
}
|
|
96775
|
+
const json = await res.json();
|
|
96776
|
+
if (json.data?.status === "error") {
|
|
96777
|
+
const errorType = json.data.details?.error;
|
|
96778
|
+
logger27.warn("push.send.error", {
|
|
96779
|
+
pushToken: pushToken.slice(0, 20) + "...",
|
|
96780
|
+
error: json.data.message,
|
|
96781
|
+
errorType
|
|
96782
|
+
});
|
|
96783
|
+
return {
|
|
96784
|
+
delivered: false,
|
|
96785
|
+
error: `${errorType ?? "unknown"}: ${json.data.message ?? ""}`
|
|
96786
|
+
};
|
|
96787
|
+
}
|
|
96788
|
+
logger27.info("push.send.ok", {
|
|
96789
|
+
motebitId: payload.motebit_id,
|
|
96790
|
+
pendingCount: payload.pending_count
|
|
96791
|
+
});
|
|
96792
|
+
return { delivered: true };
|
|
96793
|
+
} catch (err2) {
|
|
96794
|
+
const msg = err2 instanceof Error ? err2.message : String(err2);
|
|
96795
|
+
logger27.warn("push.send.failed", { error: msg });
|
|
96796
|
+
return { delivered: false, error: msg };
|
|
96797
|
+
}
|
|
96798
|
+
}
|
|
96799
|
+
};
|
|
96511
96800
|
var pushRateLimit = /* @__PURE__ */ new Map();
|
|
96512
96801
|
var PUSH_RATE_LIMIT_MS = 3e4;
|
|
96513
96802
|
async function attemptPushWake(motebitId, deps) {
|
|
@@ -96542,6 +96831,7 @@ async function attemptPushWake(motebitId, deps) {
|
|
|
96542
96831
|
|
|
96543
96832
|
// ../../services/relay/dist/tasks.js
|
|
96544
96833
|
init_task_routing();
|
|
96834
|
+
init_receipts_store();
|
|
96545
96835
|
|
|
96546
96836
|
// ../../services/relay/dist/multihop-depth.js
|
|
96547
96837
|
init_esm_shims();
|
|
@@ -99516,6 +99806,8 @@ async function createSyncRelay(config) {
|
|
|
99516
99806
|
const sweepInterval = startSweepLoop(moteDb.db, {}, () => getEmergencyFreeze());
|
|
99517
99807
|
logger36.info("sweep.started", { intervalMs: 3e5 });
|
|
99518
99808
|
const batchWithdrawalInterval = startBatchWithdrawalLoop(moteDb.db, railRegistry.list(), {}, () => getEmergencyFreeze());
|
|
99809
|
+
const pushAdapter = process.env.EXPO_ACCESS_TOKEN ? new ExpoPushAdapter({ accessToken: process.env.EXPO_ACCESS_TOKEN }) : void 0;
|
|
99810
|
+
logger36.info("relay.push_wake", { enabled: pushAdapter !== void 0 });
|
|
99519
99811
|
await registerTaskRoutes({
|
|
99520
99812
|
app,
|
|
99521
99813
|
moteDb,
|
|
@@ -99535,7 +99827,8 @@ async function createSyncRelay(config) {
|
|
|
99535
99827
|
isTokenBlacklisted,
|
|
99536
99828
|
isAgentRevoked,
|
|
99537
99829
|
platformFeeRate,
|
|
99538
|
-
railRegistry
|
|
99830
|
+
railRegistry,
|
|
99831
|
+
pushAdapter
|
|
99539
99832
|
});
|
|
99540
99833
|
function getConnectionCount() {
|
|
99541
99834
|
let count2 = 0;
|