@agentguard-run/spend 0.15.6 → 0.15.8
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/README.md +61 -1
- package/dist/anchor/index.d.ts +35 -0
- package/dist/anchor/index.d.ts.map +1 -0
- package/dist/anchor/index.js +80 -0
- package/dist/anchor/index.js.map +1 -0
- package/dist/anchor/opentimestamps.d.ts +55 -0
- package/dist/anchor/opentimestamps.d.ts.map +1 -0
- package/dist/anchor/opentimestamps.js +179 -0
- package/dist/anchor/opentimestamps.js.map +1 -0
- package/dist/anchor/store.d.ts +25 -0
- package/dist/anchor/store.d.ts.map +1 -0
- package/dist/anchor/store.js +112 -0
- package/dist/anchor/store.js.map +1 -0
- package/dist/anchor/types.d.ts +84 -0
- package/dist/anchor/types.d.ts.map +1 -0
- package/dist/anchor/types.js +28 -0
- package/dist/anchor/types.js.map +1 -0
- package/dist/cli/anchor.d.ts +10 -0
- package/dist/cli/anchor.d.ts.map +1 -0
- package/dist/cli/anchor.js +126 -0
- package/dist/cli/anchor.js.map +1 -0
- package/dist/cli/colors.d.ts +1 -0
- package/dist/cli/colors.d.ts.map +1 -1
- package/dist/cli/colors.js +4 -0
- package/dist/cli/colors.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +15 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/sample.d.ts +43 -0
- package/dist/cli/sample.d.ts.map +1 -0
- package/dist/cli/sample.js +154 -0
- package/dist/cli/sample.js.map +1 -0
- package/dist/cli/serve.d.ts +1 -1
- package/dist/cli/serve.d.ts.map +1 -1
- package/dist/cli/serve.js +80 -5
- package/dist/cli/serve.js.map +1 -1
- package/dist/cli/usage.d.ts +13 -0
- package/dist/cli/usage.d.ts.map +1 -0
- package/dist/cli/usage.js +122 -0
- package/dist/cli/usage.js.map +1 -0
- package/dist/cli/verify.d.ts.map +1 -1
- package/dist/cli/verify.js +57 -0
- package/dist/cli/verify.js.map +1 -1
- package/dist/dashboard/aggregate.d.ts +2 -0
- package/dist/dashboard/aggregate.d.ts.map +1 -1
- package/dist/dashboard/aggregate.js.map +1 -1
- package/dist/decision-log.d.ts +28 -0
- package/dist/decision-log.d.ts.map +1 -1
- package/dist/decision-log.js +34 -0
- package/dist/decision-log.js.map +1 -1
- package/dist/free-tier/usage.d.ts +104 -0
- package/dist/free-tier/usage.d.ts.map +1 -0
- package/dist/free-tier/usage.js +250 -0
- package/dist/free-tier/usage.js.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -4
- package/dist/index.js.map +1 -1
- package/dist/samples/flows.d.ts +28 -0
- package/dist/samples/flows.d.ts.map +1 -0
- package/dist/samples/flows.js +151 -0
- package/dist/samples/flows.js.map +1 -0
- package/dist/samples/verifier-html.d.ts +29 -0
- package/dist/samples/verifier-html.d.ts.map +1 -0
- package/dist/samples/verifier-html.js +188 -0
- package/dist/samples/verifier-html.js.map +1 -0
- package/package.json +1 -1
- package/src/bindings/bindings-enforce.test.ts +97 -0
- package/src/dashboard/aggregate.ts +8 -1
package/README.md
CHANGED
|
@@ -187,10 +187,70 @@ Each template sets recommended OpenRouter model assignments, capability tier, fa
|
|
|
187
187
|
|
|
188
188
|
## Provider bindings
|
|
189
189
|
|
|
190
|
-
TypeScript includes native OpenAI, Anthropic, and Bedrock bindings. Streaming usage is settled from provider usage events when available, with local token-estimator fallback when usage is missing. Settlement entries are signed into the same hash chain as enforcement decisions.
|
|
190
|
+
TypeScript includes native OpenAI, Anthropic, and Bedrock bindings. Each wraps a direct provider client in one line, with per-provider cost tables and response-shape handling. Enforcement semantics are identical across all three (and the OpenRouter path): the same policy blocks, downgrades, shadows, or allows a call before any bytes leave the process. Streaming usage is settled from provider usage events when available, with local token-estimator fallback when usage is missing. Settlement entries are signed into the same hash chain as enforcement decisions.
|
|
191
|
+
|
|
192
|
+
Wrap a direct **OpenAI** client:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import OpenAI from 'openai';
|
|
196
|
+
import { withSpendGuard } from '@agentguard-run/spend';
|
|
197
|
+
|
|
198
|
+
const openai = withSpendGuard(new OpenAI(), { policy, scope: { tenantId: 'acme' } });
|
|
199
|
+
await openai.chat.completions.create({ model: 'gpt-5', messages });
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Wrap a direct **Anthropic** client:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
206
|
+
import { withSpendGuardAnthropic } from '@agentguard-run/spend';
|
|
207
|
+
|
|
208
|
+
const anthropic = withSpendGuardAnthropic(new Anthropic(), { policy, scope: { tenantId: 'acme' } });
|
|
209
|
+
await anthropic.messages.create({ model: 'claude-sonnet-4-6', max_tokens: 1024, messages });
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Wrap a direct **Bedrock** runtime client:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { BedrockRuntimeClient } from '@aws-sdk/client-bedrock-runtime';
|
|
216
|
+
import { withSpendGuardBedrock } from '@agentguard-run/spend';
|
|
217
|
+
|
|
218
|
+
const bedrock = withSpendGuardBedrock(new BedrockRuntimeClient({ region: 'us-east-1' }), { policy, scope: { tenantId: 'acme' } });
|
|
219
|
+
await bedrock.send(new InvokeModelCommand({ modelId: 'anthropic.claude-sonnet-4-v1:0', body }));
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Only `InvokeModel` / `InvokeModelWithResponseStream` commands are gated; other Bedrock commands pass through untouched.
|
|
191
223
|
|
|
192
224
|
Python includes OpenAI, Anthropic, Bedrock, LangChain, CrewAI, and LlamaIndex integration helpers.
|
|
193
225
|
|
|
226
|
+
## Sample receipts for outreach
|
|
227
|
+
|
|
228
|
+
Generate a realistic, signed sample receipt for a named flow plus a self-contained, self-verifying HTML page — "exactly what your counterparty would get":
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
agentguard sample --flow x402-payment # or: refund | agent-purchase
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This writes `receipt.json` and `verify.html`. The HTML re-derives the entry hash from canonical JSON (SHA-256) and verifies the Ed25519 signature in-browser via WebCrypto, with NO network calls; where a browser lacks WebCrypto Ed25519 it still checks the hash chain and links to agentguard.run/verify with the receipt prefilled. The signature is real; only the flow parameters are illustrative.
|
|
235
|
+
|
|
236
|
+
## External timestamp anchoring (opt-in)
|
|
237
|
+
|
|
238
|
+
Signed receipts prove internal order and integrity, but the signatures are produced in your own environment. Anchoring the chain head's SHA-256 to a public timestamp calendar (OpenTimestamps → Bitcoin) adds an EXTERNAL clock, so "this chain existed no later than <time>" is provable to a third party.
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
AGENTGUARD_ANCHOR=opentimestamps agentguard anchor --tenant my-tenant
|
|
242
|
+
agentguard verify --trace ~/.agentguard/my-tenant/decisions.ndjson
|
|
243
|
+
# → chain head submitted to external timestamp calendar at <time>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Anchoring is OFF by default; nothing changes for existing users. **Zero data plane is preserved: only the 32-byte SHA-256 of the chain head leaves the machine.** A hash reveals nothing about prompts, completions, keys, amounts, or model identity. The proof is stored alongside the ledger at `~/.agentguard/<tenant>/anchor.json` (+ `anchor.ots`).
|
|
247
|
+
|
|
248
|
+
Note: full Bitcoin-attestation verification requires the `opentimestamps` npm package, which is not yet bundled. Without it, submitted proofs are reported as `pending` (calendar commitment received; external attestation not yet confirmed) — never as a fabricated attested time.
|
|
249
|
+
|
|
250
|
+
## Completeness: sequence-gap detection
|
|
251
|
+
|
|
252
|
+
Each signed entry carries a monotonic per-tenant sequence number. `agentguard verify` and the `agentguard serve` integrity badge flag missing sequence numbers as `possible omitted actions: sequence gap at N` — evidence that an action may have been taken around the SDK and not logged, or that a logged entry was deleted. This is detection and reporting only; it never changes spend-cap enforcement.
|
|
253
|
+
|
|
194
254
|
## No proxy
|
|
195
255
|
|
|
196
256
|
AgentGuard Spend is a library, not a gateway. It does not proxy traffic, store prompts, hold provider keys, or host policy state. The signed log lives in your storage.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External timestamp anchoring: public surface + factory.
|
|
3
|
+
*
|
|
4
|
+
* Opt-in only. `createAnchorFromEnv()` returns null unless AGENTGUARD_ANCHOR is
|
|
5
|
+
* set (today: "opentimestamps"). Nothing in the default SDK path anchors, so
|
|
6
|
+
* existing users see zero behavior change.
|
|
7
|
+
*
|
|
8
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
9
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
10
|
+
* 64/071,781; 64/071,789).
|
|
11
|
+
*/
|
|
12
|
+
import type { SignedDecisionLogEntry } from '../types';
|
|
13
|
+
import type { TimestampAnchor } from './types';
|
|
14
|
+
import { type OpenTimestampsAnchorOptions } from './opentimestamps';
|
|
15
|
+
export type { AnchorProof, AnchorStatus, AnchorVerification, TimestampAnchor } from './types';
|
|
16
|
+
export { OpenTimestampsAnchor, DEFAULT_OTS_CALENDARS, type OtsSubmit, type OpenTimestampsAnchorOptions } from './opentimestamps';
|
|
17
|
+
export { writeAnchorProof, readAnchorIndex, readAnchorIndexAt, findAnchorForHead, anchorIndexPath, anchorProofPath, anchorDir, } from './store';
|
|
18
|
+
/** Build a named anchor. Extend the switch as adapters are added. */
|
|
19
|
+
export declare function createAnchor(type: string, options?: OpenTimestampsAnchorOptions): TimestampAnchor | null;
|
|
20
|
+
/** Resolve the configured anchor from AGENTGUARD_ANCHOR, or null when unset. */
|
|
21
|
+
export declare function createAnchorFromEnv(options?: OpenTimestampsAnchorOptions): TimestampAnchor | null;
|
|
22
|
+
/**
|
|
23
|
+
* The digest an anchor should submit for a ledger: the SHA-256 of the current
|
|
24
|
+
* chain HEAD entry hash. Anchoring the head transitively commits to every prior
|
|
25
|
+
* entry (each entry binds its predecessor's hash), so a single 32-byte digest
|
|
26
|
+
* externally timestamps the whole chain. Returns null for an empty chain.
|
|
27
|
+
*/
|
|
28
|
+
export declare function chainHeadDigestHex(entries: ReadonlyArray<Pick<SignedDecisionLogEntry, 'sequence' | 'entryHash'>>): string | null;
|
|
29
|
+
/**
|
|
30
|
+
* A Merkle root over the ndjson chain's entry hashes — an alternative anchor
|
|
31
|
+
* digest when you want the anchored value to be independent of chain order
|
|
32
|
+
* bugs. Uses SHA-256 with duplicate-last-node padding.
|
|
33
|
+
*/
|
|
34
|
+
export declare function merkleRootHex(entries: ReadonlyArray<Pick<SignedDecisionLogEntry, 'entryHash'>>): string | null;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/anchor/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAE1F,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC9F,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,KAAK,SAAS,EAAE,KAAK,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AACjI,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,SAAS,GACV,MAAM,SAAS,CAAC;AAEjB,qEAAqE;AACrE,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,2BAAgC,GAAG,eAAe,GAAG,IAAI,CAG5G;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,2BAAgC,GAAG,eAAe,GAAG,IAAI,CAIrG;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,sBAAsB,EAAE,UAAU,GAAG,WAAW,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAKhI;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,CAAC,IAAI,CAAC,sBAAsB,EAAE,WAAW,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,CAa9G"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* External timestamp anchoring: public surface + factory.
|
|
4
|
+
*
|
|
5
|
+
* Opt-in only. `createAnchorFromEnv()` returns null unless AGENTGUARD_ANCHOR is
|
|
6
|
+
* set (today: "opentimestamps"). Nothing in the default SDK path anchors, so
|
|
7
|
+
* existing users see zero behavior change.
|
|
8
|
+
*
|
|
9
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
10
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
11
|
+
* 64/071,781; 64/071,789).
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.anchorDir = exports.anchorProofPath = exports.anchorIndexPath = exports.findAnchorForHead = exports.readAnchorIndexAt = exports.readAnchorIndex = exports.writeAnchorProof = exports.DEFAULT_OTS_CALENDARS = exports.OpenTimestampsAnchor = void 0;
|
|
15
|
+
exports.createAnchor = createAnchor;
|
|
16
|
+
exports.createAnchorFromEnv = createAnchorFromEnv;
|
|
17
|
+
exports.chainHeadDigestHex = chainHeadDigestHex;
|
|
18
|
+
exports.merkleRootHex = merkleRootHex;
|
|
19
|
+
const crypto_1 = require("crypto");
|
|
20
|
+
const opentimestamps_1 = require("./opentimestamps");
|
|
21
|
+
var opentimestamps_2 = require("./opentimestamps");
|
|
22
|
+
Object.defineProperty(exports, "OpenTimestampsAnchor", { enumerable: true, get: function () { return opentimestamps_2.OpenTimestampsAnchor; } });
|
|
23
|
+
Object.defineProperty(exports, "DEFAULT_OTS_CALENDARS", { enumerable: true, get: function () { return opentimestamps_2.DEFAULT_OTS_CALENDARS; } });
|
|
24
|
+
var store_1 = require("./store");
|
|
25
|
+
Object.defineProperty(exports, "writeAnchorProof", { enumerable: true, get: function () { return store_1.writeAnchorProof; } });
|
|
26
|
+
Object.defineProperty(exports, "readAnchorIndex", { enumerable: true, get: function () { return store_1.readAnchorIndex; } });
|
|
27
|
+
Object.defineProperty(exports, "readAnchorIndexAt", { enumerable: true, get: function () { return store_1.readAnchorIndexAt; } });
|
|
28
|
+
Object.defineProperty(exports, "findAnchorForHead", { enumerable: true, get: function () { return store_1.findAnchorForHead; } });
|
|
29
|
+
Object.defineProperty(exports, "anchorIndexPath", { enumerable: true, get: function () { return store_1.anchorIndexPath; } });
|
|
30
|
+
Object.defineProperty(exports, "anchorProofPath", { enumerable: true, get: function () { return store_1.anchorProofPath; } });
|
|
31
|
+
Object.defineProperty(exports, "anchorDir", { enumerable: true, get: function () { return store_1.anchorDir; } });
|
|
32
|
+
/** Build a named anchor. Extend the switch as adapters are added. */
|
|
33
|
+
function createAnchor(type, options = {}) {
|
|
34
|
+
if (type === 'opentimestamps')
|
|
35
|
+
return new opentimestamps_1.OpenTimestampsAnchor(options);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
/** Resolve the configured anchor from AGENTGUARD_ANCHOR, or null when unset. */
|
|
39
|
+
function createAnchorFromEnv(options = {}) {
|
|
40
|
+
const type = (process.env.AGENTGUARD_ANCHOR || '').trim().toLowerCase();
|
|
41
|
+
if (!type || type === 'off' || type === 'none' || type === '0')
|
|
42
|
+
return null;
|
|
43
|
+
return createAnchor(type, options);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The digest an anchor should submit for a ledger: the SHA-256 of the current
|
|
47
|
+
* chain HEAD entry hash. Anchoring the head transitively commits to every prior
|
|
48
|
+
* entry (each entry binds its predecessor's hash), so a single 32-byte digest
|
|
49
|
+
* externally timestamps the whole chain. Returns null for an empty chain.
|
|
50
|
+
*/
|
|
51
|
+
function chainHeadDigestHex(entries) {
|
|
52
|
+
if (entries.length === 0)
|
|
53
|
+
return null;
|
|
54
|
+
let head = entries[0];
|
|
55
|
+
for (const e of entries)
|
|
56
|
+
if (e.sequence >= head.sequence)
|
|
57
|
+
head = e;
|
|
58
|
+
return (0, crypto_1.createHash)('sha256').update(head.entryHash, 'utf8').digest('hex');
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* A Merkle root over the ndjson chain's entry hashes — an alternative anchor
|
|
62
|
+
* digest when you want the anchored value to be independent of chain order
|
|
63
|
+
* bugs. Uses SHA-256 with duplicate-last-node padding.
|
|
64
|
+
*/
|
|
65
|
+
function merkleRootHex(entries) {
|
|
66
|
+
if (entries.length === 0)
|
|
67
|
+
return null;
|
|
68
|
+
let level = entries.map((e) => new Uint8Array((0, crypto_1.createHash)('sha256').update(e.entryHash, 'utf8').digest()));
|
|
69
|
+
while (level.length > 1) {
|
|
70
|
+
const next = [];
|
|
71
|
+
for (let i = 0; i < level.length; i += 2) {
|
|
72
|
+
const left = level[i];
|
|
73
|
+
const right = level[i + 1] ?? left;
|
|
74
|
+
next.push(new Uint8Array((0, crypto_1.createHash)('sha256').update(Buffer.concat([left, right])).digest()));
|
|
75
|
+
}
|
|
76
|
+
level = next;
|
|
77
|
+
}
|
|
78
|
+
return Buffer.from(level[0]).toString('hex');
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/anchor/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTimestamps anchor adapter.
|
|
3
|
+
*
|
|
4
|
+
* Submits ONLY the 32-byte SHA-256 of the chain head to one or more public
|
|
5
|
+
* OpenTimestamps calendar servers and stores the returned commitment. Nothing
|
|
6
|
+
* else leaves the process (zero data plane): a hash is submitted, a proof comes
|
|
7
|
+
* back.
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT HONESTY NOTE (read before relying on this in production):
|
|
10
|
+
* The canonical OpenTimestamps `.ots` serialization and the Bitcoin-attestation
|
|
11
|
+
* *upgrade + verification* require the `opentimestamps` npm package, which is
|
|
12
|
+
* NOT a dependency of this package today. Without it, this adapter:
|
|
13
|
+
* - DOES perform a real calendar submission (POST <calendar>/digest) and
|
|
14
|
+
* store the raw calendar response bytes, and
|
|
15
|
+
* - DOES bind the proof to the exact head hash it anchored,
|
|
16
|
+
* but it does NOT parse the .ots structure or confirm the Bitcoin attestation.
|
|
17
|
+
* Such proofs are therefore reported as `status: 'pending'` with
|
|
18
|
+
* `attested: false`. The OTS wire/verification protocol is STUBBED pending the
|
|
19
|
+
* `opentimestamps` dependency. We never fabricate an attested time.
|
|
20
|
+
*
|
|
21
|
+
* To complete the chain to Bitcoin: add `opentimestamps` and upgrade the stored
|
|
22
|
+
* proof (`ots upgrade`), then `attestedAt` can be populated from the real
|
|
23
|
+
* Bitcoin block time. This adapter reads `attestedAt` if present but never sets
|
|
24
|
+
* it without a real upgrade.
|
|
25
|
+
*
|
|
26
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
27
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
28
|
+
* 64/071,781; 64/071,789).
|
|
29
|
+
*/
|
|
30
|
+
import type { AnchorProof, AnchorVerification, TimestampAnchor } from './types';
|
|
31
|
+
/** Public OpenTimestamps calendar servers (aggregators). */
|
|
32
|
+
export declare const DEFAULT_OTS_CALENDARS: string[];
|
|
33
|
+
/**
|
|
34
|
+
* Transport hook: submit a digest to one calendar, return the raw response
|
|
35
|
+
* bytes (the calendar's commitment). Injectable for tests / private mirrors.
|
|
36
|
+
* Should reject on network/HTTP failure.
|
|
37
|
+
*/
|
|
38
|
+
export type OtsSubmit = (calendarUrl: string, digest: Uint8Array) => Promise<Uint8Array>;
|
|
39
|
+
export interface OpenTimestampsAnchorOptions {
|
|
40
|
+
calendars?: string[];
|
|
41
|
+
/** Override the calendar transport (defaults to a real https POST). */
|
|
42
|
+
submit?: OtsSubmit;
|
|
43
|
+
/** Require at least one calendar to succeed. Defaults to true. */
|
|
44
|
+
requireCalendar?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export declare class OpenTimestampsAnchor implements TimestampAnchor {
|
|
47
|
+
readonly type = "opentimestamps";
|
|
48
|
+
private readonly calendars;
|
|
49
|
+
private readonly submit;
|
|
50
|
+
private readonly requireCalendar;
|
|
51
|
+
constructor(options?: OpenTimestampsAnchorOptions);
|
|
52
|
+
anchor(headHashHex: string): Promise<AnchorProof>;
|
|
53
|
+
verify(headHashHex: string, proof: AnchorProof): Promise<AnchorVerification>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=opentimestamps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opentimestamps.d.ts","sourceRoot":"","sources":["../../src/anchor/opentimestamps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEhF,4DAA4D;AAC5D,eAAO,MAAM,qBAAqB,UAGjC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAEzF,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,uEAAuE;IACvE,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,kEAAkE;IAClE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAiDD,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,QAAQ,CAAC,IAAI,oBAAoB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;gBAE9B,OAAO,GAAE,2BAAgC;IAM/C,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAwCjD,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAiCnF"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OpenTimestamps anchor adapter.
|
|
4
|
+
*
|
|
5
|
+
* Submits ONLY the 32-byte SHA-256 of the chain head to one or more public
|
|
6
|
+
* OpenTimestamps calendar servers and stores the returned commitment. Nothing
|
|
7
|
+
* else leaves the process (zero data plane): a hash is submitted, a proof comes
|
|
8
|
+
* back.
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT HONESTY NOTE (read before relying on this in production):
|
|
11
|
+
* The canonical OpenTimestamps `.ots` serialization and the Bitcoin-attestation
|
|
12
|
+
* *upgrade + verification* require the `opentimestamps` npm package, which is
|
|
13
|
+
* NOT a dependency of this package today. Without it, this adapter:
|
|
14
|
+
* - DOES perform a real calendar submission (POST <calendar>/digest) and
|
|
15
|
+
* store the raw calendar response bytes, and
|
|
16
|
+
* - DOES bind the proof to the exact head hash it anchored,
|
|
17
|
+
* but it does NOT parse the .ots structure or confirm the Bitcoin attestation.
|
|
18
|
+
* Such proofs are therefore reported as `status: 'pending'` with
|
|
19
|
+
* `attested: false`. The OTS wire/verification protocol is STUBBED pending the
|
|
20
|
+
* `opentimestamps` dependency. We never fabricate an attested time.
|
|
21
|
+
*
|
|
22
|
+
* To complete the chain to Bitcoin: add `opentimestamps` and upgrade the stored
|
|
23
|
+
* proof (`ots upgrade`), then `attestedAt` can be populated from the real
|
|
24
|
+
* Bitcoin block time. This adapter reads `attestedAt` if present but never sets
|
|
25
|
+
* it without a real upgrade.
|
|
26
|
+
*
|
|
27
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
28
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
29
|
+
* 64/071,781; 64/071,789).
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.OpenTimestampsAnchor = exports.DEFAULT_OTS_CALENDARS = void 0;
|
|
33
|
+
/** Public OpenTimestamps calendar servers (aggregators). */
|
|
34
|
+
exports.DEFAULT_OTS_CALENDARS = [
|
|
35
|
+
'https://alice.btc.calendar.opentimestamps.org',
|
|
36
|
+
'https://bob.btc.calendar.opentimestamps.org',
|
|
37
|
+
];
|
|
38
|
+
function hexToBytes(hex) {
|
|
39
|
+
const clean = hex.trim().toLowerCase();
|
|
40
|
+
if (!/^[0-9a-f]*$/.test(clean) || clean.length % 2 !== 0) {
|
|
41
|
+
throw new Error('OpenTimestampsAnchor: headHashHex must be lowercase hex of even length');
|
|
42
|
+
}
|
|
43
|
+
const out = new Uint8Array(clean.length / 2);
|
|
44
|
+
for (let i = 0; i < out.length; i++)
|
|
45
|
+
out[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/** Default transport: POST the raw digest bytes to <calendar>/digest over https. */
|
|
49
|
+
const defaultSubmit = (calendarUrl, digest) => new Promise((resolve, reject) => {
|
|
50
|
+
// Lazy require so the SDK never pulls node:https at import time in bundlers.
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
52
|
+
const https = require('https');
|
|
53
|
+
const url = new URL(calendarUrl.replace(/\/+$/, '') + '/digest');
|
|
54
|
+
const req = https.request({
|
|
55
|
+
hostname: url.hostname,
|
|
56
|
+
port: url.port || 443,
|
|
57
|
+
path: url.pathname,
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/octet-stream',
|
|
61
|
+
'Accept': 'application/vnd.opentimestamps.v1',
|
|
62
|
+
'Content-Length': digest.length,
|
|
63
|
+
'User-Agent': 'agentguard-spend',
|
|
64
|
+
},
|
|
65
|
+
}, (res) => {
|
|
66
|
+
const chunks = [];
|
|
67
|
+
res.on('data', (c) => chunks.push(c));
|
|
68
|
+
res.on('end', () => {
|
|
69
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
70
|
+
resolve(new Uint8Array(Buffer.concat(chunks)));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
reject(new Error(`OTS calendar ${calendarUrl} returned HTTP ${res.statusCode}`));
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
req.on('error', reject);
|
|
78
|
+
req.write(Buffer.from(digest));
|
|
79
|
+
req.end();
|
|
80
|
+
});
|
|
81
|
+
class OpenTimestampsAnchor {
|
|
82
|
+
type = 'opentimestamps';
|
|
83
|
+
calendars;
|
|
84
|
+
submit;
|
|
85
|
+
requireCalendar;
|
|
86
|
+
constructor(options = {}) {
|
|
87
|
+
this.calendars = options.calendars ?? exports.DEFAULT_OTS_CALENDARS;
|
|
88
|
+
this.submit = options.submit ?? defaultSubmit;
|
|
89
|
+
this.requireCalendar = options.requireCalendar ?? true;
|
|
90
|
+
}
|
|
91
|
+
async anchor(headHashHex) {
|
|
92
|
+
const digest = hexToBytes(headHashHex);
|
|
93
|
+
if (digest.length !== 32) {
|
|
94
|
+
throw new Error('OpenTimestampsAnchor: chain head digest must be SHA-256 (32 bytes)');
|
|
95
|
+
}
|
|
96
|
+
const responses = [];
|
|
97
|
+
const used = [];
|
|
98
|
+
const errors = [];
|
|
99
|
+
for (const calendar of this.calendars) {
|
|
100
|
+
try {
|
|
101
|
+
const bytes = await this.submit(calendar, digest);
|
|
102
|
+
responses.push(bytes);
|
|
103
|
+
used.push(calendar);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
errors.push(`${calendar}: ${err instanceof Error ? err.message : String(err)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (responses.length === 0 && this.requireCalendar) {
|
|
110
|
+
throw new Error('OpenTimestampsAnchor: no calendar accepted the digest: ' + errors.join('; '));
|
|
111
|
+
}
|
|
112
|
+
// Concatenate raw calendar commitments length-prefixed so verify can bind
|
|
113
|
+
// them back to the head. This is NOT a canonical .ots file — see the module
|
|
114
|
+
// note. It is a real, non-fabricated record of what each calendar returned.
|
|
115
|
+
const proofBytes = concatLengthPrefixed(responses);
|
|
116
|
+
return {
|
|
117
|
+
version: 1,
|
|
118
|
+
anchorType: this.type,
|
|
119
|
+
headHashHex: headHashHex.trim().toLowerCase(),
|
|
120
|
+
submittedAt: new Date().toISOString(),
|
|
121
|
+
proof: Buffer.from(proofBytes).toString('base64'),
|
|
122
|
+
status: 'pending',
|
|
123
|
+
calendars: used,
|
|
124
|
+
attestedAt: null,
|
|
125
|
+
note: 'Calendar commitment stored. Bitcoin attestation upgrade + verification ' +
|
|
126
|
+
'require the `opentimestamps` npm package (not bundled); OTS .ots ' +
|
|
127
|
+
'serialization is stubbed pending that dependency. Only a SHA-256 hash was sent.',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async verify(headHashHex, proof) {
|
|
131
|
+
const head = headHashHex.trim().toLowerCase();
|
|
132
|
+
if (proof.anchorType !== this.type) {
|
|
133
|
+
return { ok: false, status: 'invalid', when: null, attested: false, detail: `not an ${this.type} proof` };
|
|
134
|
+
}
|
|
135
|
+
if (proof.headHashHex.trim().toLowerCase() !== head) {
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
status: 'invalid',
|
|
139
|
+
when: null,
|
|
140
|
+
attested: false,
|
|
141
|
+
detail: 'proof does not bind the supplied chain head hash',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// A real Bitcoin-attested upgrade populates attestedAt; we trust it only if
|
|
145
|
+
// present (never synthesised here).
|
|
146
|
+
if (proof.attestedAt) {
|
|
147
|
+
return { ok: true, status: 'complete', when: proof.attestedAt, attested: true };
|
|
148
|
+
}
|
|
149
|
+
const hasCommitment = typeof proof.proof === 'string' && proof.proof.length > 0;
|
|
150
|
+
if (!hasCommitment && !(proof.calendars && proof.calendars.length > 0)) {
|
|
151
|
+
return { ok: false, status: 'invalid', when: null, attested: false, detail: 'proof carries no calendar commitment' };
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
status: 'pending',
|
|
156
|
+
when: proof.submittedAt ?? null,
|
|
157
|
+
attested: false,
|
|
158
|
+
detail: 'Calendar commitment present for this head hash; Bitcoin attestation not yet ' +
|
|
159
|
+
'confirmed (requires `opentimestamps` upgrade). `when` is the submission time, not an attested time.',
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.OpenTimestampsAnchor = OpenTimestampsAnchor;
|
|
164
|
+
function concatLengthPrefixed(chunks) {
|
|
165
|
+
let total = 0;
|
|
166
|
+
for (const c of chunks)
|
|
167
|
+
total += 4 + c.length;
|
|
168
|
+
const out = new Uint8Array(total);
|
|
169
|
+
const view = new DataView(out.buffer);
|
|
170
|
+
let offset = 0;
|
|
171
|
+
for (const c of chunks) {
|
|
172
|
+
view.setUint32(offset, c.length, false);
|
|
173
|
+
offset += 4;
|
|
174
|
+
out.set(c, offset);
|
|
175
|
+
offset += c.length;
|
|
176
|
+
}
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=opentimestamps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opentimestamps.js","sourceRoot":"","sources":["../../src/anchor/opentimestamps.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anchor proof persistence, stored ALONGSIDE the ndjson ledger so a verifier
|
|
3
|
+
* can find both together:
|
|
4
|
+
* ~/.agentguard/<tenant>/anchor.ots latest raw calendar proof bytes (binary)
|
|
5
|
+
* ~/.agentguard/<tenant>/anchor.json small append-only index of AnchorProof
|
|
6
|
+
*
|
|
7
|
+
* The index is what `agentguard verify` reads to report external timestamping.
|
|
8
|
+
*
|
|
9
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
10
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
11
|
+
* 64/071,781; 64/071,789).
|
|
12
|
+
*/
|
|
13
|
+
import type { AnchorProof } from './types';
|
|
14
|
+
export declare function defaultHome(): string;
|
|
15
|
+
export declare function anchorDir(tenant: string, home?: string): string;
|
|
16
|
+
export declare function anchorIndexPath(tenant: string, home?: string): string;
|
|
17
|
+
export declare function anchorProofPath(tenant: string, home?: string): string;
|
|
18
|
+
/** Append a proof to the tenant index and write its raw bytes to anchor.ots. */
|
|
19
|
+
export declare function writeAnchorProof(tenant: string, proof: AnchorProof, home?: string): void;
|
|
20
|
+
export declare function readAnchorIndex(tenant: string, home?: string): AnchorProof[];
|
|
21
|
+
/** Read an anchor index directly from a path (used by verify beside a trace). */
|
|
22
|
+
export declare function readAnchorIndexAt(indexPath: string): AnchorProof[];
|
|
23
|
+
/** The most recent proof recorded for a given head hash, if any. */
|
|
24
|
+
export declare function findAnchorForHead(proofs: AnchorProof[], headHashHex: string): AnchorProof | null;
|
|
25
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/anchor/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,MAAsB,GAAG,MAAM,CAG9E;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAWxF;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,CAE5E;AAED,iFAAiF;AACjF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,EAAE,CASlE;AAED,oEAAoE;AACpE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAMhG"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Anchor proof persistence, stored ALONGSIDE the ndjson ledger so a verifier
|
|
4
|
+
* can find both together:
|
|
5
|
+
* ~/.agentguard/<tenant>/anchor.ots latest raw calendar proof bytes (binary)
|
|
6
|
+
* ~/.agentguard/<tenant>/anchor.json small append-only index of AnchorProof
|
|
7
|
+
*
|
|
8
|
+
* The index is what `agentguard verify` reads to report external timestamping.
|
|
9
|
+
*
|
|
10
|
+
* Patent notice: Protected by U.S. patent-pending technology
|
|
11
|
+
* (App. Nos. 63/983,615; 63/983,621; 63/983,843; 63/984,626;
|
|
12
|
+
* 64/071,781; 64/071,789).
|
|
13
|
+
*/
|
|
14
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
17
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
18
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
19
|
+
}
|
|
20
|
+
Object.defineProperty(o, k2, desc);
|
|
21
|
+
}) : (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
o[k2] = m[k];
|
|
24
|
+
}));
|
|
25
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
26
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
27
|
+
}) : function(o, v) {
|
|
28
|
+
o["default"] = v;
|
|
29
|
+
});
|
|
30
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
31
|
+
var ownKeys = function(o) {
|
|
32
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
33
|
+
var ar = [];
|
|
34
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
35
|
+
return ar;
|
|
36
|
+
};
|
|
37
|
+
return ownKeys(o);
|
|
38
|
+
};
|
|
39
|
+
return function (mod) {
|
|
40
|
+
if (mod && mod.__esModule) return mod;
|
|
41
|
+
var result = {};
|
|
42
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
43
|
+
__setModuleDefault(result, mod);
|
|
44
|
+
return result;
|
|
45
|
+
};
|
|
46
|
+
})();
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.defaultHome = defaultHome;
|
|
49
|
+
exports.anchorDir = anchorDir;
|
|
50
|
+
exports.anchorIndexPath = anchorIndexPath;
|
|
51
|
+
exports.anchorProofPath = anchorProofPath;
|
|
52
|
+
exports.writeAnchorProof = writeAnchorProof;
|
|
53
|
+
exports.readAnchorIndex = readAnchorIndex;
|
|
54
|
+
exports.readAnchorIndexAt = readAnchorIndexAt;
|
|
55
|
+
exports.findAnchorForHead = findAnchorForHead;
|
|
56
|
+
const fs = __importStar(require("fs"));
|
|
57
|
+
const os = __importStar(require("os"));
|
|
58
|
+
const path = __importStar(require("path"));
|
|
59
|
+
function defaultHome() {
|
|
60
|
+
return process.env.AGENTGUARD_HOME || path.join(os.homedir(), '.agentguard');
|
|
61
|
+
}
|
|
62
|
+
function anchorDir(tenant, home = defaultHome()) {
|
|
63
|
+
const clean = String(tenant || 'default').replace(/[^A-Za-z0-9._-]/g, '_') || 'default';
|
|
64
|
+
return path.join(home, clean);
|
|
65
|
+
}
|
|
66
|
+
function anchorIndexPath(tenant, home) {
|
|
67
|
+
return path.join(anchorDir(tenant, home), 'anchor.json');
|
|
68
|
+
}
|
|
69
|
+
function anchorProofPath(tenant, home) {
|
|
70
|
+
return path.join(anchorDir(tenant, home), 'anchor.ots');
|
|
71
|
+
}
|
|
72
|
+
/** Append a proof to the tenant index and write its raw bytes to anchor.ots. */
|
|
73
|
+
function writeAnchorProof(tenant, proof, home) {
|
|
74
|
+
const dir = anchorDir(tenant, home);
|
|
75
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
76
|
+
const existing = readAnchorIndex(tenant, home);
|
|
77
|
+
existing.push(proof);
|
|
78
|
+
fs.writeFileSync(anchorIndexPath(tenant, home), JSON.stringify(existing, null, 2) + '\n');
|
|
79
|
+
try {
|
|
80
|
+
fs.writeFileSync(anchorProofPath(tenant, home), Buffer.from(proof.proof, 'base64'));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
// best-effort binary sidecar; the JSON index is the source of truth
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function readAnchorIndex(tenant, home) {
|
|
87
|
+
return readAnchorIndexAt(anchorIndexPath(tenant, home));
|
|
88
|
+
}
|
|
89
|
+
/** Read an anchor index directly from a path (used by verify beside a trace). */
|
|
90
|
+
function readAnchorIndexAt(indexPath) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|
93
|
+
if (Array.isArray(parsed))
|
|
94
|
+
return parsed;
|
|
95
|
+
if (parsed && typeof parsed === 'object')
|
|
96
|
+
return [parsed];
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return [];
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** The most recent proof recorded for a given head hash, if any. */
|
|
104
|
+
function findAnchorForHead(proofs, headHashHex) {
|
|
105
|
+
const head = headHashHex.trim().toLowerCase();
|
|
106
|
+
for (let i = proofs.length - 1; i >= 0; i--) {
|
|
107
|
+
if (proofs[i]?.headHashHex?.trim().toLowerCase() === head)
|
|
108
|
+
return proofs[i];
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/anchor/store.ts"],"names":[],"mappings":"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"}
|