@totemsdk/provider-bond 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listProvidersByServiceType = exports.getProvider = exports.listProviders = exports.updateProviderScore = exports.recordProviderIncident = exports.recordProviderProbe = exports.attachBondProof = exports.updateProviderManifest = exports.registerProvider = exports.createEmptyProviderBondRegistryState = exports.explainProviderPolicyMatch = exports.rankProvidersByPolicy = exports.filterProvidersByPolicy = exports.computeProviderRecommendation = exports.computeProviderScore = exports.rejectIncident = exports.resolveIncident = exports.acknowledgeIncident = exports.recordIncident = exports.recordHeartbeat = exports.recordProbe = exports.verifyBondStack = exports.assertBondMeetsMinimum = exports.verifyBondProof = exports.assertProviderControlsAddress = exports.verifyProviderBondAddresses = exports.verifyProviderManifestIdentity = exports.bindProviderManifestToIdentity = exports.assertManifestNotExpired = exports.verifyProviderBondManifest = exports.computeProviderBondManifestHash = exports.computeProviderBondExtensionHash = exports.createProviderBondManifest = exports.parseProviderBondRecord = exports.serializeProviderBondRecord = exports.parseProviderBondState = exports.serializeProviderBondState = exports.DEFAULT_PROVIDER_SCORING_WEIGHTS = exports.PROVIDER_BOND_TOPIC_PREFIX = exports.DEFAULT_MINIMA_TOKEN_ID = exports.ProviderSerializationError = exports.ProviderRegistryError = exports.ProviderPolicyError = exports.ProviderScoreError = exports.IncidentError = exports.ProbeError = exports.BondProofError = exports.ProviderIdentityError = exports.ProviderManifestError = exports.ProviderBondError = void 0;
4
+ exports.MemoryProviderBondStore = exports.listOfflineProviders = exports.listRiskyProviders = void 0;
5
+ var errors_js_1 = require("./errors.js");
6
+ Object.defineProperty(exports, "ProviderBondError", { enumerable: true, get: function () { return errors_js_1.ProviderBondError; } });
7
+ Object.defineProperty(exports, "ProviderManifestError", { enumerable: true, get: function () { return errors_js_1.ProviderManifestError; } });
8
+ Object.defineProperty(exports, "ProviderIdentityError", { enumerable: true, get: function () { return errors_js_1.ProviderIdentityError; } });
9
+ Object.defineProperty(exports, "BondProofError", { enumerable: true, get: function () { return errors_js_1.BondProofError; } });
10
+ Object.defineProperty(exports, "ProbeError", { enumerable: true, get: function () { return errors_js_1.ProbeError; } });
11
+ Object.defineProperty(exports, "IncidentError", { enumerable: true, get: function () { return errors_js_1.IncidentError; } });
12
+ Object.defineProperty(exports, "ProviderScoreError", { enumerable: true, get: function () { return errors_js_1.ProviderScoreError; } });
13
+ Object.defineProperty(exports, "ProviderPolicyError", { enumerable: true, get: function () { return errors_js_1.ProviderPolicyError; } });
14
+ Object.defineProperty(exports, "ProviderRegistryError", { enumerable: true, get: function () { return errors_js_1.ProviderRegistryError; } });
15
+ Object.defineProperty(exports, "ProviderSerializationError", { enumerable: true, get: function () { return errors_js_1.ProviderSerializationError; } });
16
+ var constants_js_1 = require("./constants.js");
17
+ Object.defineProperty(exports, "DEFAULT_MINIMA_TOKEN_ID", { enumerable: true, get: function () { return constants_js_1.DEFAULT_MINIMA_TOKEN_ID; } });
18
+ Object.defineProperty(exports, "PROVIDER_BOND_TOPIC_PREFIX", { enumerable: true, get: function () { return constants_js_1.PROVIDER_BOND_TOPIC_PREFIX; } });
19
+ Object.defineProperty(exports, "DEFAULT_PROVIDER_SCORING_WEIGHTS", { enumerable: true, get: function () { return constants_js_1.DEFAULT_PROVIDER_SCORING_WEIGHTS; } });
20
+ var serialization_js_1 = require("./serialization.js");
21
+ Object.defineProperty(exports, "serializeProviderBondState", { enumerable: true, get: function () { return serialization_js_1.serializeProviderBondState; } });
22
+ Object.defineProperty(exports, "parseProviderBondState", { enumerable: true, get: function () { return serialization_js_1.parseProviderBondState; } });
23
+ Object.defineProperty(exports, "serializeProviderBondRecord", { enumerable: true, get: function () { return serialization_js_1.serializeProviderBondRecord; } });
24
+ Object.defineProperty(exports, "parseProviderBondRecord", { enumerable: true, get: function () { return serialization_js_1.parseProviderBondRecord; } });
25
+ var manifest_js_1 = require("./manifest.js");
26
+ Object.defineProperty(exports, "createProviderBondManifest", { enumerable: true, get: function () { return manifest_js_1.createProviderBondManifest; } });
27
+ Object.defineProperty(exports, "computeProviderBondExtensionHash", { enumerable: true, get: function () { return manifest_js_1.computeProviderBondExtensionHash; } });
28
+ Object.defineProperty(exports, "computeProviderBondManifestHash", { enumerable: true, get: function () { return manifest_js_1.computeProviderBondManifestHash; } });
29
+ Object.defineProperty(exports, "verifyProviderBondManifest", { enumerable: true, get: function () { return manifest_js_1.verifyProviderBondManifest; } });
30
+ Object.defineProperty(exports, "assertManifestNotExpired", { enumerable: true, get: function () { return manifest_js_1.assertManifestNotExpired; } });
31
+ var identity_js_1 = require("./identity.js");
32
+ Object.defineProperty(exports, "bindProviderManifestToIdentity", { enumerable: true, get: function () { return identity_js_1.bindProviderManifestToIdentity; } });
33
+ Object.defineProperty(exports, "verifyProviderManifestIdentity", { enumerable: true, get: function () { return identity_js_1.verifyProviderManifestIdentity; } });
34
+ Object.defineProperty(exports, "verifyProviderBondAddresses", { enumerable: true, get: function () { return identity_js_1.verifyProviderBondAddresses; } });
35
+ Object.defineProperty(exports, "assertProviderControlsAddress", { enumerable: true, get: function () { return identity_js_1.assertProviderControlsAddress; } });
36
+ var bond_proof_js_1 = require("./bond-proof.js");
37
+ Object.defineProperty(exports, "verifyBondProof", { enumerable: true, get: function () { return bond_proof_js_1.verifyBondProof; } });
38
+ Object.defineProperty(exports, "assertBondMeetsMinimum", { enumerable: true, get: function () { return bond_proof_js_1.assertBondMeetsMinimum; } });
39
+ Object.defineProperty(exports, "verifyBondStack", { enumerable: true, get: function () { return bond_proof_js_1.verifyBondStack; } });
40
+ var probes_js_1 = require("./probes.js");
41
+ Object.defineProperty(exports, "recordProbe", { enumerable: true, get: function () { return probes_js_1.recordProbe; } });
42
+ Object.defineProperty(exports, "recordHeartbeat", { enumerable: true, get: function () { return probes_js_1.recordHeartbeat; } });
43
+ var incidents_js_1 = require("./incidents.js");
44
+ Object.defineProperty(exports, "recordIncident", { enumerable: true, get: function () { return incidents_js_1.recordIncident; } });
45
+ Object.defineProperty(exports, "acknowledgeIncident", { enumerable: true, get: function () { return incidents_js_1.acknowledgeIncident; } });
46
+ Object.defineProperty(exports, "resolveIncident", { enumerable: true, get: function () { return incidents_js_1.resolveIncident; } });
47
+ Object.defineProperty(exports, "rejectIncident", { enumerable: true, get: function () { return incidents_js_1.rejectIncident; } });
48
+ var scoring_js_1 = require("./scoring.js");
49
+ Object.defineProperty(exports, "computeProviderScore", { enumerable: true, get: function () { return scoring_js_1.computeProviderScore; } });
50
+ Object.defineProperty(exports, "computeProviderRecommendation", { enumerable: true, get: function () { return scoring_js_1.computeProviderRecommendation; } });
51
+ var policy_js_1 = require("./policy.js");
52
+ Object.defineProperty(exports, "filterProvidersByPolicy", { enumerable: true, get: function () { return policy_js_1.filterProvidersByPolicy; } });
53
+ Object.defineProperty(exports, "rankProvidersByPolicy", { enumerable: true, get: function () { return policy_js_1.rankProvidersByPolicy; } });
54
+ Object.defineProperty(exports, "explainProviderPolicyMatch", { enumerable: true, get: function () { return policy_js_1.explainProviderPolicyMatch; } });
55
+ var registry_js_1 = require("./registry.js");
56
+ Object.defineProperty(exports, "createEmptyProviderBondRegistryState", { enumerable: true, get: function () { return registry_js_1.createEmptyProviderBondRegistryState; } });
57
+ Object.defineProperty(exports, "registerProvider", { enumerable: true, get: function () { return registry_js_1.registerProvider; } });
58
+ Object.defineProperty(exports, "updateProviderManifest", { enumerable: true, get: function () { return registry_js_1.updateProviderManifest; } });
59
+ Object.defineProperty(exports, "attachBondProof", { enumerable: true, get: function () { return registry_js_1.attachBondProof; } });
60
+ Object.defineProperty(exports, "recordProviderProbe", { enumerable: true, get: function () { return registry_js_1.recordProviderProbe; } });
61
+ Object.defineProperty(exports, "recordProviderIncident", { enumerable: true, get: function () { return registry_js_1.recordProviderIncident; } });
62
+ Object.defineProperty(exports, "updateProviderScore", { enumerable: true, get: function () { return registry_js_1.updateProviderScore; } });
63
+ Object.defineProperty(exports, "listProviders", { enumerable: true, get: function () { return registry_js_1.listProviders; } });
64
+ Object.defineProperty(exports, "getProvider", { enumerable: true, get: function () { return registry_js_1.getProvider; } });
65
+ Object.defineProperty(exports, "listProvidersByServiceType", { enumerable: true, get: function () { return registry_js_1.listProvidersByServiceType; } });
66
+ Object.defineProperty(exports, "listRiskyProviders", { enumerable: true, get: function () { return registry_js_1.listRiskyProviders; } });
67
+ Object.defineProperty(exports, "listOfflineProviders", { enumerable: true, get: function () { return registry_js_1.listOfflineProviders; } });
68
+ var memory_store_js_1 = require("./memory-store.js");
69
+ Object.defineProperty(exports, "MemoryProviderBondStore", { enumerable: true, get: function () { return memory_store_js_1.MemoryProviderBondStore; } });
@@ -0,0 +1,6 @@
1
+ import type { ProviderBondManifest, ProviderBondExtension, ProviderBondVerifyResult, CreateProviderBondManifestParams, VerifyProviderBondManifestParams } from './types.js';
2
+ export declare function createProviderBondManifest(params: CreateProviderBondManifestParams): ProviderBondManifest;
3
+ export declare function computeProviderBondExtensionHash(extension: ProviderBondExtension): string;
4
+ export declare function computeProviderBondManifestHash(manifest: ProviderBondManifest): string;
5
+ export declare function verifyProviderBondManifest(params: VerifyProviderBondManifestParams): ProviderBondVerifyResult;
6
+ export declare function assertManifestNotExpired(manifest: ProviderBondManifest, now?: number): void;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createProviderBondManifest = createProviderBondManifest;
4
+ exports.computeProviderBondExtensionHash = computeProviderBondExtensionHash;
5
+ exports.computeProviderBondManifestHash = computeProviderBondManifestHash;
6
+ exports.verifyProviderBondManifest = verifyProviderBondManifest;
7
+ exports.assertManifestNotExpired = assertManifestNotExpired;
8
+ const core_1 = require("@totemsdk/core");
9
+ const manifest_1 = require("@totemsdk/manifest");
10
+ const errors_js_1 = require("./errors.js");
11
+ const serialization_js_1 = require("./serialization.js");
12
+ function hexToBytes(hex) {
13
+ return (0, core_1.hexToBytes)(hex);
14
+ }
15
+ function bytesToHex(bytes) {
16
+ return (0, core_1.bytesToHex)(bytes);
17
+ }
18
+ function createProviderBondManifest(params) {
19
+ const edgeServiceManifestId = (0, manifest_1.computeManifestId)(params.edgeService);
20
+ return {
21
+ edgeServiceManifestId,
22
+ edgeService: params.edgeService,
23
+ signedEdgeService: params.signedEdgeService,
24
+ providerBond: params.providerBond,
25
+ };
26
+ }
27
+ function computeProviderBondExtensionHash(extension) {
28
+ const { extensionHash, ...rest } = extension;
29
+ const json = (0, serialization_js_1.canonicalJson)(rest);
30
+ return bytesToHex((0, core_1.F)(new TextEncoder().encode(json)));
31
+ }
32
+ function computeProviderBondManifestHash(manifest) {
33
+ const { signedEdgeService, ...rest } = manifest;
34
+ const json = (0, serialization_js_1.canonicalJson)(rest);
35
+ return bytesToHex((0, core_1.F)(new TextEncoder().encode(json)));
36
+ }
37
+ function verifyProviderBondManifest(params) {
38
+ const { manifest, now } = params;
39
+ if (manifest.signedEdgeService) {
40
+ const result = (0, manifest_1.verifyManifest)(manifest.signedEdgeService);
41
+ if (!result.valid) {
42
+ return { ok: false, reason: result.reason || 'Manifest signature invalid', code: 'MANIFEST_SIGNATURE_INVALID' };
43
+ }
44
+ }
45
+ if (now !== undefined && manifest.edgeService.expiresAt !== undefined && manifest.edgeService.expiresAt < now) {
46
+ return { ok: false, reason: 'Manifest has expired', code: 'MANIFEST_EXPIRED' };
47
+ }
48
+ if (manifest.providerBond.extensionHash) {
49
+ const computed = computeProviderBondExtensionHash(manifest.providerBond);
50
+ if (computed !== manifest.providerBond.extensionHash) {
51
+ return { ok: false, reason: 'Provider bond extension hash mismatch', code: 'BOND_EXTENSION_HASH_MISMATCH' };
52
+ }
53
+ }
54
+ return { ok: true, code: 'OK' };
55
+ }
56
+ function assertManifestNotExpired(manifest, now) {
57
+ const ts = now ?? Date.now();
58
+ if (manifest.edgeService.expiresAt !== undefined && manifest.edgeService.expiresAt < ts) {
59
+ throw new errors_js_1.ProviderManifestError('Manifest has expired', 'MANIFEST_EXPIRED');
60
+ }
61
+ }
@@ -0,0 +1,17 @@
1
+ import type { ProviderBondManifest, ProviderBondRegistryState, BondProofRef, ProbeResult, IncidentRecord, ProviderScore } from './types.js';
2
+ export declare class MemoryProviderBondStore {
3
+ private state;
4
+ constructor();
5
+ registerProvider(manifest: ProviderBondManifest): Promise<void>;
6
+ updateProviderManifest(manifest: ProviderBondManifest): Promise<void>;
7
+ attachBondProof(providerId: string, proof: BondProofRef): Promise<void>;
8
+ recordProbe(providerId: string, probe: ProbeResult): Promise<void>;
9
+ recordIncident(providerId: string, incident: IncidentRecord): Promise<void>;
10
+ updateScore(providerId: string, score: ProviderScore): Promise<void>;
11
+ listProviders(): Promise<ProviderBondManifest[]>;
12
+ getProvider(providerId: string): Promise<ProviderBondManifest | undefined>;
13
+ listProvidersByServiceType(serviceType: string): Promise<ProviderBondManifest[]>;
14
+ listRiskyProviders(threshold: number): Promise<ProviderBondManifest[]>;
15
+ listOfflineProviders(maxHeartbeatAgeMs: number, now: number): Promise<ProviderBondManifest[]>;
16
+ getSnapshot(): Promise<ProviderBondRegistryState>;
17
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryProviderBondStore = void 0;
4
+ const registry_js_1 = require("./registry.js");
5
+ class MemoryProviderBondStore {
6
+ constructor() {
7
+ this.state = (0, registry_js_1.createEmptyProviderBondRegistryState)();
8
+ }
9
+ async registerProvider(manifest) {
10
+ this.state = (0, registry_js_1.registerProvider)(this.state, manifest);
11
+ }
12
+ async updateProviderManifest(manifest) {
13
+ this.state = (0, registry_js_1.updateProviderManifest)(this.state, manifest);
14
+ }
15
+ async attachBondProof(providerId, proof) {
16
+ this.state = (0, registry_js_1.attachBondProof)(this.state, providerId, proof);
17
+ }
18
+ async recordProbe(providerId, probe) {
19
+ this.state = (0, registry_js_1.recordProviderProbe)(this.state, providerId, probe);
20
+ }
21
+ async recordIncident(providerId, incident) {
22
+ this.state = (0, registry_js_1.recordProviderIncident)(this.state, providerId, incident);
23
+ }
24
+ async updateScore(providerId, score) {
25
+ this.state = (0, registry_js_1.updateProviderScore)(this.state, providerId, score);
26
+ }
27
+ async listProviders() {
28
+ return (0, registry_js_1.listProviders)(this.state);
29
+ }
30
+ async getProvider(providerId) {
31
+ return (0, registry_js_1.getProvider)(this.state, providerId);
32
+ }
33
+ async listProvidersByServiceType(serviceType) {
34
+ return (0, registry_js_1.listProvidersByServiceType)(this.state, serviceType);
35
+ }
36
+ async listRiskyProviders(threshold) {
37
+ return (0, registry_js_1.listRiskyProviders)(this.state, threshold);
38
+ }
39
+ async listOfflineProviders(maxHeartbeatAgeMs, now) {
40
+ return (0, registry_js_1.listOfflineProviders)(this.state, maxHeartbeatAgeMs, now);
41
+ }
42
+ async getSnapshot() {
43
+ return JSON.parse(JSON.stringify(this.state, (_, v) => typeof v === 'bigint' ? v.toString() : v));
44
+ }
45
+ }
46
+ exports.MemoryProviderBondStore = MemoryProviderBondStore;
@@ -0,0 +1,4 @@
1
+ import type { ProviderBondRegistryState, ProviderPolicy, PolicyMatch } from './types.js';
2
+ export declare function filterProvidersByPolicy(state: ProviderBondRegistryState, policy: ProviderPolicy): PolicyMatch[];
3
+ export declare function rankProvidersByPolicy(matches: PolicyMatch[]): PolicyMatch[];
4
+ export declare function explainProviderPolicyMatch(match: PolicyMatch): string[];
package/dist/policy.js ADDED
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterProvidersByPolicy = filterProvidersByPolicy;
4
+ exports.rankProvidersByPolicy = rankProvidersByPolicy;
5
+ exports.explainProviderPolicyMatch = explainProviderPolicyMatch;
6
+ function filterProvidersByPolicy(state, policy) {
7
+ const providers = Object.values(state.providers);
8
+ const now = policy.now ?? Date.now();
9
+ return providers.map((provider) => {
10
+ const reasons = [];
11
+ const failures = [];
12
+ const score = state.scores[provider.providerBond.providerId];
13
+ if (policy.serviceType && provider.edgeService.serviceType !== policy.serviceType) {
14
+ failures.push(`Service type ${provider.edgeService.serviceType} does not match required ${policy.serviceType}`);
15
+ }
16
+ if (policy.minScore !== undefined && score) {
17
+ if (score.score < policy.minScore) {
18
+ failures.push(`Score ${score.score} is below minimum ${policy.minScore}`);
19
+ }
20
+ else {
21
+ reasons.push(`Score ${score.score} meets minimum ${policy.minScore}`);
22
+ }
23
+ }
24
+ if (policy.requireIdentity) {
25
+ if (!provider.providerBond.bondOwnerAddress) {
26
+ failures.push('Identity not declared');
27
+ }
28
+ else {
29
+ reasons.push('Identity declared');
30
+ }
31
+ }
32
+ if (policy.requireActiveBond) {
33
+ const hasActiveBond = provider.providerBond.bondStack?.some((d) => d.status === 'active');
34
+ if (!hasActiveBond) {
35
+ failures.push('No active bond');
36
+ }
37
+ else {
38
+ reasons.push('Active bond present');
39
+ }
40
+ }
41
+ if (policy.requireMinimaHardCollateral) {
42
+ const hasMinimaHardCollateral = provider.providerBond.bondStack?.some((d) => d.asset === 'MINIMA' && d.purpose === 'hard-collateral' && d.status === 'active');
43
+ if (!hasMinimaHardCollateral) {
44
+ failures.push('No active MINIMA hard-collateral bond');
45
+ }
46
+ else {
47
+ reasons.push('MINIMA hard-collateral bond present');
48
+ }
49
+ }
50
+ if (policy.minBondAmount !== undefined) {
51
+ const totalBond = provider.providerBond.bondStack?.reduce((sum, d) => sum + d.amount, 0n) ?? 0n;
52
+ if (totalBond < policy.minBondAmount) {
53
+ failures.push(`Total bond ${totalBond.toString()} is below minimum ${policy.minBondAmount.toString()}`);
54
+ }
55
+ else {
56
+ reasons.push(`Total bond ${totalBond.toString()} meets minimum ${policy.minBondAmount.toString()}`);
57
+ }
58
+ }
59
+ if (policy.acceptedAssets && policy.acceptedAssets.length > 0) {
60
+ const bondAssets = new Set(provider.providerBond.bondStack?.map((d) => d.asset) ?? []);
61
+ const hasAccepted = policy.acceptedAssets.some((a) => bondAssets.has(a));
62
+ if (!hasAccepted) {
63
+ failures.push('No bond in accepted assets');
64
+ }
65
+ else {
66
+ reasons.push('Bond asset accepted');
67
+ }
68
+ }
69
+ if (policy.acceptedPurposes && policy.acceptedPurposes.length > 0) {
70
+ const bondPurposes = new Set(provider.providerBond.bondStack?.map((d) => d.purpose) ?? []);
71
+ const hasAccepted = policy.acceptedPurposes.some((p) => bondPurposes.has(p));
72
+ if (!hasAccepted) {
73
+ failures.push('No bond with accepted purpose');
74
+ }
75
+ else {
76
+ reasons.push('Bond purpose accepted');
77
+ }
78
+ }
79
+ if (policy.maxIncidentSeverity) {
80
+ const providerIncidents = state.incidents[provider.providerBond.providerId] ?? [];
81
+ const severityOrder = { low: 0, medium: 1, high: 2, critical: 3 };
82
+ const maxSeen = providerIncidents.reduce((max, i) => {
83
+ const s = severityOrder[i.severity] ?? 0;
84
+ return s > max ? s : max;
85
+ }, 0);
86
+ const policyMax = severityOrder[policy.maxIncidentSeverity] ?? 0;
87
+ if (maxSeen > policyMax) {
88
+ failures.push(`Incident severity exceeds policy maximum`);
89
+ }
90
+ else {
91
+ reasons.push('Incident severity within policy limits');
92
+ }
93
+ }
94
+ if (policy.maxHeartbeatAgeMs !== undefined) {
95
+ const providerProbes = state.probes[provider.providerBond.providerId] ?? [];
96
+ const heartbeats = providerProbes.filter((p) => p.type === 'heartbeat');
97
+ const lastHeartbeat = heartbeats.length > 0
98
+ ? heartbeats.reduce((max, h) => h.observedAt > max ? h.observedAt : max, 0)
99
+ : 0;
100
+ if (now - lastHeartbeat > policy.maxHeartbeatAgeMs) {
101
+ failures.push(`Last heartbeat too old (${now - lastHeartbeat}ms > ${policy.maxHeartbeatAgeMs}ms)`);
102
+ }
103
+ else if (lastHeartbeat > 0) {
104
+ reasons.push('Heartbeat is recent');
105
+ }
106
+ }
107
+ return {
108
+ providerId: provider.providerBond.providerId,
109
+ provider,
110
+ matched: failures.length === 0,
111
+ score,
112
+ reasons,
113
+ failures,
114
+ };
115
+ });
116
+ }
117
+ function rankProvidersByPolicy(matches) {
118
+ return [...matches].sort((a, b) => {
119
+ if (a.matched && !b.matched)
120
+ return -1;
121
+ if (!a.matched && b.matched)
122
+ return 1;
123
+ const scoreA = a.score?.score ?? 0;
124
+ const scoreB = b.score?.score ?? 0;
125
+ return scoreB - scoreA;
126
+ });
127
+ }
128
+ function explainProviderPolicyMatch(match) {
129
+ const lines = [];
130
+ lines.push(`Provider: ${match.providerId}`);
131
+ lines.push(`Matched: ${match.matched}`);
132
+ if (match.score) {
133
+ lines.push(`Score: ${match.score.score} (${match.score.recommendation})`);
134
+ }
135
+ if (match.reasons.length > 0) {
136
+ lines.push('Passed checks:');
137
+ for (const r of match.reasons)
138
+ lines.push(` + ${r}`);
139
+ }
140
+ if (match.failures.length > 0) {
141
+ lines.push('Failed checks:');
142
+ for (const f of match.failures)
143
+ lines.push(` - ${f}`);
144
+ }
145
+ return lines;
146
+ }
@@ -0,0 +1,3 @@
1
+ import type { ProbeResult, RecordProbeParams } from './types.js';
2
+ export declare function recordProbe(params: RecordProbeParams): ProbeResult;
3
+ export declare function recordHeartbeat(providerId: string, now?: number): ProbeResult;
package/dist/probes.js ADDED
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.recordProbe = recordProbe;
4
+ exports.recordHeartbeat = recordHeartbeat;
5
+ let probeCounter = 0;
6
+ function recordProbe(params) {
7
+ const now = params.now ?? Date.now();
8
+ probeCounter++;
9
+ return {
10
+ probeId: `probe-${now}-${probeCounter}`,
11
+ providerId: params.providerId,
12
+ type: params.type,
13
+ ok: params.ok,
14
+ latencyMs: params.latencyMs,
15
+ observedAt: now,
16
+ message: params.message,
17
+ metadata: params.metadata,
18
+ };
19
+ }
20
+ function recordHeartbeat(providerId, now) {
21
+ return recordProbe({
22
+ providerId,
23
+ type: 'heartbeat',
24
+ ok: true,
25
+ now,
26
+ });
27
+ }
@@ -0,0 +1,13 @@
1
+ import type { ProviderBondRegistryState, ProviderBondManifest, BondProofRef, ProbeResult, IncidentRecord, ProviderScore } from './types.js';
2
+ export declare function createEmptyProviderBondRegistryState(): ProviderBondRegistryState;
3
+ export declare function registerProvider(state: ProviderBondRegistryState, manifest: ProviderBondManifest): ProviderBondRegistryState;
4
+ export declare function updateProviderManifest(state: ProviderBondRegistryState, manifest: ProviderBondManifest): ProviderBondRegistryState;
5
+ export declare function attachBondProof(state: ProviderBondRegistryState, providerId: string, proof: BondProofRef): ProviderBondRegistryState;
6
+ export declare function recordProviderProbe(state: ProviderBondRegistryState, providerId: string, probe: ProbeResult): ProviderBondRegistryState;
7
+ export declare function recordProviderIncident(state: ProviderBondRegistryState, providerId: string, incident: IncidentRecord): ProviderBondRegistryState;
8
+ export declare function updateProviderScore(state: ProviderBondRegistryState, providerId: string, score: ProviderScore): ProviderBondRegistryState;
9
+ export declare function listProviders(state: ProviderBondRegistryState): ProviderBondManifest[];
10
+ export declare function getProvider(state: ProviderBondRegistryState, providerId: string): ProviderBondManifest | undefined;
11
+ export declare function listProvidersByServiceType(state: ProviderBondRegistryState, serviceType: string): ProviderBondManifest[];
12
+ export declare function listRiskyProviders(state: ProviderBondRegistryState, threshold: number): ProviderBondManifest[];
13
+ export declare function listOfflineProviders(state: ProviderBondRegistryState, maxHeartbeatAgeMs: number, now: number): ProviderBondManifest[];
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createEmptyProviderBondRegistryState = createEmptyProviderBondRegistryState;
4
+ exports.registerProvider = registerProvider;
5
+ exports.updateProviderManifest = updateProviderManifest;
6
+ exports.attachBondProof = attachBondProof;
7
+ exports.recordProviderProbe = recordProviderProbe;
8
+ exports.recordProviderIncident = recordProviderIncident;
9
+ exports.updateProviderScore = updateProviderScore;
10
+ exports.listProviders = listProviders;
11
+ exports.getProvider = getProvider;
12
+ exports.listProvidersByServiceType = listProvidersByServiceType;
13
+ exports.listRiskyProviders = listRiskyProviders;
14
+ exports.listOfflineProviders = listOfflineProviders;
15
+ function createEmptyProviderBondRegistryState() {
16
+ return {
17
+ providers: {},
18
+ bondProofs: {},
19
+ probes: {},
20
+ incidents: {},
21
+ scores: {},
22
+ updatedAt: Date.now(),
23
+ };
24
+ }
25
+ function registerProvider(state, manifest) {
26
+ const providers = { ...state.providers };
27
+ providers[manifest.providerBond.providerId] = manifest;
28
+ return { ...state, providers, updatedAt: Date.now() };
29
+ }
30
+ function updateProviderManifest(state, manifest) {
31
+ return registerProvider(state, manifest);
32
+ }
33
+ function attachBondProof(state, providerId, proof) {
34
+ const bondProofs = { ...state.bondProofs };
35
+ const existing = bondProofs[providerId] ?? [];
36
+ bondProofs[providerId] = [...existing, proof];
37
+ return { ...state, bondProofs, updatedAt: Date.now() };
38
+ }
39
+ function recordProviderProbe(state, providerId, probe) {
40
+ const probes = { ...state.probes };
41
+ const existing = probes[providerId] ?? [];
42
+ probes[providerId] = [...existing, probe];
43
+ return { ...state, probes, updatedAt: Date.now() };
44
+ }
45
+ function recordProviderIncident(state, providerId, incident) {
46
+ const incidents = { ...state.incidents };
47
+ const existing = incidents[providerId] ?? [];
48
+ incidents[providerId] = [...existing, incident];
49
+ return { ...state, incidents, updatedAt: Date.now() };
50
+ }
51
+ function updateProviderScore(state, providerId, score) {
52
+ const scores = { ...state.scores };
53
+ scores[providerId] = score;
54
+ return { ...state, scores, updatedAt: Date.now() };
55
+ }
56
+ function listProviders(state) {
57
+ return Object.values(state.providers);
58
+ }
59
+ function getProvider(state, providerId) {
60
+ return state.providers[providerId];
61
+ }
62
+ function listProvidersByServiceType(state, serviceType) {
63
+ return Object.values(state.providers).filter((p) => p.edgeService.serviceType === serviceType);
64
+ }
65
+ function listRiskyProviders(state, threshold) {
66
+ return Object.values(state.providers).filter((p) => {
67
+ const score = state.scores[p.providerBond.providerId];
68
+ return score && score.score < threshold;
69
+ });
70
+ }
71
+ function listOfflineProviders(state, maxHeartbeatAgeMs, now) {
72
+ return Object.values(state.providers).filter((p) => {
73
+ const providerProbes = state.probes[p.providerBond.providerId] ?? [];
74
+ const heartbeats = providerProbes.filter((pr) => pr.type === 'heartbeat');
75
+ if (heartbeats.length === 0)
76
+ return true;
77
+ const lastHeartbeat = heartbeats.reduce((max, h) => h.observedAt > max ? h.observedAt : max, 0);
78
+ return now - lastHeartbeat > maxHeartbeatAgeMs;
79
+ });
80
+ }
@@ -0,0 +1,3 @@
1
+ import type { ProviderScore, ProviderRecommendation, ComputeProviderScoreParams } from './types.js';
2
+ export declare function computeProviderScore(params: ComputeProviderScoreParams): ProviderScore;
3
+ export declare function computeProviderRecommendation(score: number): ProviderRecommendation;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeProviderScore = computeProviderScore;
4
+ exports.computeProviderRecommendation = computeProviderRecommendation;
5
+ const constants_js_1 = require("./constants.js");
6
+ function computeProviderScore(params) {
7
+ const { provider, bondProofs, probes, incidents, now, weights } = params;
8
+ const w = weights ?? constants_js_1.DEFAULT_PROVIDER_SCORING_WEIGHTS;
9
+ const ts = now ?? Date.now();
10
+ const reasons = [];
11
+ let identityScore = 0;
12
+ if (provider.providerBond.bondOwnerAddress) {
13
+ identityScore = 100;
14
+ reasons.push('Identity address declared');
15
+ }
16
+ else {
17
+ reasons.push('No identity address declared');
18
+ }
19
+ let bondScore = 0;
20
+ const hasBond = provider.providerBond.bondStack && provider.providerBond.bondStack.length > 0;
21
+ const hasMinimaBond = hasBond && provider.providerBond.bondStack.some((d) => d.asset === 'MINIMA' && d.purpose === 'hard-collateral' && d.status === 'active');
22
+ const hasProof = bondProofs && bondProofs.length > 0;
23
+ if (hasMinimaBond && hasProof) {
24
+ bondScore = 100;
25
+ reasons.push('Active MINIMA hard-collateral bond with proof');
26
+ }
27
+ else if (hasMinimaBond) {
28
+ bondScore = 70;
29
+ reasons.push('MINIMA bond declared but no proof attached');
30
+ }
31
+ else if (hasBond) {
32
+ bondScore = 40;
33
+ reasons.push('Bond declared but not MINIMA hard-collateral');
34
+ }
35
+ else {
36
+ reasons.push('No bond declared');
37
+ }
38
+ let reliabilityScore = 50;
39
+ const providerProbes = probes ?? [];
40
+ if (providerProbes.length > 0) {
41
+ const recentProbes = providerProbes.filter((p) => ts - p.observedAt < constants_js_1.MAX_HEARTBEAT_AGE_MS_DEFAULT);
42
+ if (recentProbes.length > 0) {
43
+ const successRate = recentProbes.filter((p) => p.ok).length / recentProbes.length;
44
+ const avgLatency = recentProbes.reduce((sum, p) => sum + (p.latencyMs ?? 0), 0) / recentProbes.length;
45
+ reliabilityScore = successRate * 100;
46
+ if (avgLatency > 1000)
47
+ reliabilityScore -= 20;
48
+ else if (avgLatency > 500)
49
+ reliabilityScore -= 10;
50
+ reasons.push(`Probe success rate: ${(successRate * 100).toFixed(0)}%`);
51
+ }
52
+ else {
53
+ reliabilityScore = 0;
54
+ reasons.push('No recent probes (offline)');
55
+ }
56
+ }
57
+ else {
58
+ reasons.push('No probe data');
59
+ }
60
+ let incidentScore = 100;
61
+ const providerIncidents = incidents ?? [];
62
+ if (providerIncidents.length > 0) {
63
+ const openIncidents = providerIncidents.filter((i) => i.status === 'open');
64
+ const criticalIncidents = providerIncidents.filter((i) => i.severity === 'critical');
65
+ const highIncidents = providerIncidents.filter((i) => i.severity === 'high');
66
+ incidentScore = 100 - (openIncidents.length * 10) - (criticalIncidents.length * 20) - (highIncidents.length * 10);
67
+ incidentScore = Math.max(0, incidentScore);
68
+ if (criticalIncidents.length > 0)
69
+ reasons.push(`${criticalIncidents.length} critical incident(s)`);
70
+ if (highIncidents.length > 0)
71
+ reasons.push(`${highIncidents.length} high-severity incident(s)`);
72
+ if (openIncidents.length > 0)
73
+ reasons.push(`${openIncidents.length} open incident(s)`);
74
+ }
75
+ else {
76
+ reasons.push('No incidents');
77
+ }
78
+ const totalScore = Math.round(identityScore * w.identity +
79
+ bondScore * w.bond +
80
+ reliabilityScore * w.reliability +
81
+ incidentScore * w.incidents);
82
+ const recommendation = computeProviderRecommendation(totalScore);
83
+ return {
84
+ providerId: provider.providerBond.providerId,
85
+ score: totalScore,
86
+ recommendation,
87
+ bondScore,
88
+ identityScore,
89
+ reliabilityScore,
90
+ incidentScore,
91
+ computedAt: ts,
92
+ reasons,
93
+ };
94
+ }
95
+ function computeProviderRecommendation(score) {
96
+ if (score >= constants_js_1.RECOMMENDATION_THRESHOLDS.recommended)
97
+ return 'recommended';
98
+ if (score >= constants_js_1.RECOMMENDATION_THRESHOLDS.acceptable)
99
+ return 'acceptable';
100
+ if (score >= constants_js_1.RECOMMENDATION_THRESHOLDS.risky)
101
+ return 'risky';
102
+ if (score >= constants_js_1.RECOMMENDATION_THRESHOLDS.avoid)
103
+ return 'avoid';
104
+ if (score > 0)
105
+ return 'unbonded';
106
+ return 'offline';
107
+ }
@@ -0,0 +1,6 @@
1
+ declare function canonicalJson(value: unknown): string;
2
+ export declare function serializeProviderBondState(state: unknown): string;
3
+ export declare function parseProviderBondState<T = unknown>(json: string): T;
4
+ export declare function serializeProviderBondRecord(record: unknown): string;
5
+ export declare function parseProviderBondRecord<T = unknown>(json: string): T;
6
+ export { canonicalJson };