@secondlayer/shared 11.7.1 → 11.8.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/src/archive/copy-loader.d.ts +1 -1
- package/dist/src/archive/copy-loader.js +4 -20
- package/dist/src/archive/copy-loader.js.map +2 -2
- package/dist/src/archive/derived-stage-report.d.ts +1 -1
- package/dist/src/archive/derived-stage-report.js +3 -19
- package/dist/src/archive/derived-stage-report.js.map +2 -2
- package/dist/src/archive/disk-guard.d.ts +1 -1
- package/dist/src/archive/disk-guard.js +3 -19
- package/dist/src/archive/disk-guard.js.map +2 -2
- package/dist/src/archive/instance-diagnosis.d.ts +1 -1
- package/dist/src/archive/instance-diagnosis.js +6 -22
- package/dist/src/archive/instance-diagnosis.js.map +2 -2
- package/dist/src/archive/key-registry.d.ts +1 -1
- package/dist/src/archive/key-registry.js +12 -12
- package/dist/src/archive/key-registry.js.map +1 -1
- package/dist/src/archive/per-height-digest.d.ts +1 -1
- package/dist/src/archive/per-height-digest.js +3 -17
- package/dist/src/archive/per-height-digest.js.map +5 -5
- package/dist/src/archive/progress.d.ts +1 -1
- package/dist/src/archive/progress.js +4 -20
- package/dist/src/archive/progress.js.map +2 -2
- package/dist/src/archive/range-digest.d.ts +2 -3
- package/dist/src/archive/range-digest.js +4 -20
- package/dist/src/archive/range-digest.js.map +2 -2
- package/dist/src/archive/reference.d.ts +1 -1
- package/dist/src/archive/reference.js +14 -12
- package/dist/src/archive/reference.js.map +5 -5
- package/dist/src/archive/root-key.d.ts +1 -1
- package/dist/src/archive/root-key.js +3 -19
- package/dist/src/archive/root-key.js.map +2 -2
- package/dist/src/archive/semantic-digest-builder.d.ts +2 -3
- package/dist/src/archive/semantic-digest-builder.js +5 -19
- package/dist/src/archive/semantic-digest-builder.js.map +5 -5
- package/dist/src/archive/semantic-digest.d.ts +1 -1
- package/dist/src/archive/semantic-digest.js +4 -20
- package/dist/src/archive/semantic-digest.js.map +2 -2
- package/dist/src/archive/status.d.ts +1 -1
- package/dist/src/archive/status.js +4 -20
- package/dist/src/archive/status.js.map +2 -2
- package/dist/src/coverage/index.d.ts +1 -2
- package/dist/src/coverage/index.js +80 -96
- package/dist/src/coverage/index.js.map +2 -2
- package/dist/src/crypto/ed25519.d.ts +1 -1
- package/dist/src/crypto/ed25519.js +13 -13
- package/dist/src/crypto/ed25519.js.map +1 -1
- package/dist/src/crypto/hmac.d.ts +1 -1
- package/dist/src/crypto/hmac.js +9 -9
- package/dist/src/crypto/hmac.js.map +1 -1
- package/dist/src/crypto/secondlayer-webhook.d.ts +1 -1
- package/dist/src/crypto/secondlayer-webhook.js +14 -14
- package/dist/src/crypto/secondlayer-webhook.js.map +2 -2
- package/dist/src/crypto/secrets.d.ts +1 -1
- package/dist/src/crypto/secrets.js +15 -29
- package/dist/src/crypto/secrets.js.map +5 -5
- package/dist/src/crypto/standard-webhooks.d.ts +1 -1
- package/dist/src/crypto/standard-webhooks.js +3 -19
- package/dist/src/crypto/standard-webhooks.js.map +2 -2
- package/dist/src/db/index.d.ts +18 -19
- package/dist/src/db/index.js +40 -55
- package/dist/src/db/index.js.map +5 -5
- package/dist/src/db/jsonb.d.ts +1 -1
- package/dist/src/db/jsonb.js +3 -19
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/chain-reorgs.d.ts +1 -2
- package/dist/src/db/queries/chain-reorgs.js +27 -42
- package/dist/src/db/queries/chain-reorgs.js.map +5 -5
- package/dist/src/db/queries/contracts.d.ts +1 -2
- package/dist/src/db/queries/contracts.js +9 -23
- package/dist/src/db/queries/contracts.js.map +5 -5
- package/dist/src/db/queries/instance.d.ts +1 -2
- package/dist/src/db/queries/instance.js +7 -23
- package/dist/src/db/queries/instance.js.map +2 -2
- package/dist/src/db/queries/integrity.d.ts +1 -2
- package/dist/src/db/queries/integrity.js +5 -21
- package/dist/src/db/queries/integrity.js.map +2 -2
- package/dist/src/db/queries/subgraph-gaps.d.ts +1 -2
- package/dist/src/db/queries/subgraph-gaps.js +5 -21
- package/dist/src/db/queries/subgraph-gaps.js.map +2 -2
- package/dist/src/db/queries/subgraph-operations.d.ts +1 -2
- package/dist/src/db/queries/subgraph-operations.js +17 -33
- package/dist/src/db/queries/subgraph-operations.js.map +2 -2
- package/dist/src/db/queries/subgraphs.d.ts +2 -14
- package/dist/src/db/queries/subgraphs.js +15 -37
- package/dist/src/db/queries/subgraphs.js.map +5 -5
- package/dist/src/db/queries/sync-scope.d.ts +1 -2
- package/dist/src/db/queries/sync-scope.js +6 -22
- package/dist/src/db/queries/sync-scope.js.map +2 -2
- package/dist/src/db/queries/webhooks.d.ts +1 -2
- package/dist/src/db/queries/webhooks.js +54 -50
- package/dist/src/db/queries/webhooks.js.map +5 -5
- package/dist/src/db/schema.d.ts +1 -2
- package/dist/src/env.d.ts +1 -1
- package/dist/src/env.js +7 -23
- package/dist/src/env.js.map +2 -2
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js +15 -31
- package/dist/src/errors.js.map +2 -2
- package/dist/src/index-http.d.ts +1 -1
- package/dist/src/index-http.js +3 -19
- package/dist/src/index-http.js.map +2 -2
- package/dist/src/index-internal-auth.d.ts +1 -1
- package/dist/src/index-internal-auth.js +7 -23
- package/dist/src/index-internal-auth.js.map +2 -2
- package/dist/src/index.d.ts +136 -149
- package/dist/src/index.js +259 -313
- package/dist/src/index.js.map +12 -12
- package/dist/src/leader.d.ts +1 -1
- package/dist/src/leader.js +7 -21
- package/dist/src/leader.js.map +5 -5
- package/dist/src/logger.js +1 -17
- package/dist/src/logger.js.map +2 -2
- package/dist/src/mode.d.ts +1 -1
- package/dist/src/mode.js +5 -21
- package/dist/src/mode.js.map +2 -2
- package/dist/src/node/archive-client.d.ts +1 -1
- package/dist/src/node/archive-client.js +9 -23
- package/dist/src/node/archive-client.js.map +5 -5
- package/dist/src/node/client.d.ts +1 -1
- package/dist/src/node/client.js +4 -18
- package/dist/src/node/client.js.map +5 -5
- package/dist/src/node/consensus.d.ts +1 -1
- package/dist/src/node/consensus.js +5 -21
- package/dist/src/node/consensus.js.map +2 -2
- package/dist/src/node/hiro-client.d.ts +1 -1
- package/dist/src/node/hiro-client.js +1 -17
- package/dist/src/node/hiro-client.js.map +2 -2
- package/dist/src/node/hiro-pg-client.js +1 -17
- package/dist/src/node/hiro-pg-client.js.map +2 -2
- package/dist/src/node/local-client.d.ts +1 -2
- package/dist/src/node/local-client.js +3 -19
- package/dist/src/node/local-client.js.map +2 -2
- package/dist/src/node/nakamoto.d.ts +1 -1
- package/dist/src/node/nakamoto.js +12 -28
- package/dist/src/node/nakamoto.js.map +2 -2
- package/dist/src/queue/listener.d.ts +1 -1
- package/dist/src/queue/listener.js +4 -20
- package/dist/src/queue/listener.js.map +2 -2
- package/dist/src/runtime/index.d.ts +1 -1
- package/dist/src/runtime/index.js +51 -67
- package/dist/src/runtime/index.js.map +2 -2
- package/dist/src/schemas/index.d.ts +1 -4
- package/dist/src/schemas/index.js +19 -37
- package/dist/src/schemas/index.js.map +3 -3
- package/dist/src/schemas/subgraphs.d.ts +1 -4
- package/dist/src/schemas/subgraphs.js +1 -18
- package/dist/src/schemas/subgraphs.js.map +3 -3
- package/dist/src/schemas/webhooks.d.ts +1 -1
- package/dist/src/schemas/webhooks.js +21 -37
- package/dist/src/schemas/webhooks.js.map +2 -2
- package/dist/src/streams-bulk-manifest.d.ts +1 -1
- package/dist/src/streams-bulk-manifest.js +9 -9
- package/dist/src/streams-bulk-manifest.js.map +1 -1
- package/dist/src/streams-rows/index.d.ts +1 -1
- package/dist/src/streams-rows/index.js +32 -48
- package/dist/src/streams-rows/index.js.map +2 -2
- package/dist/src/subgraphs/spec.d.ts +1 -11
- package/dist/src/subgraphs/spec.js +27 -93
- package/dist/src/subgraphs/spec.js.map +3 -3
- package/dist/src/types.d.ts +1 -1
- package/migrations/0136_drop_subgraph_visibility.ts +34 -0
- package/package.json +1 -1
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
1
|
// src/archive/progress.ts
|
|
18
2
|
var DEFAULT_INTERVAL_MS = 30000;
|
|
19
3
|
function formatDuration(seconds) {
|
|
@@ -30,7 +14,7 @@ function formatDuration(seconds) {
|
|
|
30
14
|
function createProgressReporter(options) {
|
|
31
15
|
const intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
32
16
|
const now = options.now ?? (() => Date.now());
|
|
33
|
-
const write = options.write ?? ((
|
|
17
|
+
const write = options.write ?? ((line) => process.stderr.write(`${line}
|
|
34
18
|
`));
|
|
35
19
|
const startedAt = now();
|
|
36
20
|
let lastEmit = startedAt;
|
|
@@ -67,9 +51,9 @@ function createProgressReporter(options) {
|
|
|
67
51
|
};
|
|
68
52
|
}
|
|
69
53
|
export {
|
|
70
|
-
|
|
71
|
-
|
|
54
|
+
createProgressReporter,
|
|
55
|
+
formatDuration
|
|
72
56
|
};
|
|
73
57
|
|
|
74
|
-
//# debugId=
|
|
58
|
+
//# debugId=65F27E8CDC95C42364756E2164756E21
|
|
75
59
|
//# sourceMappingURL=progress.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * Progress reporting for multi-hour archive jobs.\n *\n * A job that prints nothing until it finishes is indistinguishable from a hung\n * one. That is not a cosmetic problem: on 2026-08-12 an export ran silently for\n * 2.5 hours and progress had to be inferred by listing partition filenames on\n * disk, and a killed upload left a log whose single line could not distinguish\n * \"died immediately\" from \"died at 90%\".\n *\n * Two rules make the output trustworthy:\n *\n * - **Time-based, not count-based.** Reporting every N items goes quiet for\n * an hour when items are large and floods when they are small. Reporting\n * every N seconds behaves the same for a sparse range and a dense one, so\n * silence always means the same thing: something is wrong.\n * - **stderr only.** Progress is chrome. Putting it on stdout would corrupt\n * `--json` output for every caller that pipes it.\n */\n\nexport type ProgressReporter = {\n\t/** Record cumulative progress. Emits at most once per interval. */\n\ttick: (completed: number, detail?: string) => void;\n\t/** Emit a final line regardless of interval. */\n\tfinish: (detail?: string) => void;\n};\n\nexport type ProgressOptions = {\n\tlabel: string;\n\t/** Total units, when known. Enables percentage and ETA. */\n\ttotal?: number;\n\tintervalMs?: number;\n\t/** Defaults to stderr. Injectable so tests capture without spying. */\n\twrite?: (line: string) => void;\n\tnow?: () => number;\n};\n\nconst DEFAULT_INTERVAL_MS = 30_000;\n\nexport function formatDuration(seconds: number): string {\n\tif (!Number.isFinite(seconds) || seconds < 0) return \"unknown\";\n\tif (seconds < 90) return `${Math.round(seconds)}s`;\n\tif (seconds < 5_400) return `${Math.round(seconds / 60)}m`;\n\tif (seconds < 172_800) return `${(seconds / 3_600).toFixed(1)}h`;\n\treturn `${(seconds / 86_400).toFixed(1)}d`;\n}\n\nexport function createProgressReporter(\n\toptions: ProgressOptions,\n): ProgressReporter {\n\tconst intervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;\n\tconst now = options.now ?? (() => Date.now());\n\tconst write =\n\t\toptions.write ?? ((line: string) => process.stderr.write(`${line}\\n`));\n\n\tconst startedAt = now();\n\tlet lastEmit = startedAt;\n\n\tfunction line(completed: number, detail?: string): string {\n\t\tconst elapsed = (now() - startedAt) / 1000;\n\t\tconst parts = [`${options.label}:`];\n\n\t\tif (options.total !== undefined && options.total > 0) {\n\t\t\tconst pct = Math.floor((completed / options.total) * 100);\n\t\t\tparts.push(`${completed}/${options.total} (${pct}%)`);\n\t\t\t// ETA only once there is a rate to extrapolate from; a prediction made\n\t\t\t// from zero progress is noise dressed as information.\n\t\t\tif (completed > 0 && elapsed > 0) {\n\t\t\t\tconst remaining = ((options.total - completed) / completed) * elapsed;\n\t\t\t\tparts.push(`eta ${formatDuration(remaining)}`);\n\t\t\t}\n\t\t} else {\n\t\t\tparts.push(String(completed));\n\t\t}\n\n\t\tparts.push(`elapsed ${formatDuration(elapsed)}`);\n\t\tif (detail) parts.push(`· ${detail}`);\n\t\treturn ` ${parts.join(\" \")}`;\n\t}\n\n\treturn {\n\t\ttick(completed, detail) {\n\t\t\tconst current = now();\n\t\t\tif (current - lastEmit < intervalMs) return;\n\t\t\tlastEmit = current;\n\t\t\twrite(line(completed, detail));\n\t\t},\n\t\tfinish(detail) {\n\t\t\tlastEmit = now();\n\t\t\twrite(line(options.total ?? 0, detail));\n\t\t},\n\t};\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAoCA,IAAM,sBAAsB;AAErB,SAAS,cAAc,CAAC,SAAyB;AAAA,EACvD,IAAI,CAAC,OAAO,SAAS,OAAO,KAAK,UAAU;AAAA,IAAG,OAAO;AAAA,EACrD,IAAI,UAAU;AAAA,IAAI,OAAO,GAAG,KAAK,MAAM,OAAO;AAAA,EAC9C,IAAI,UAAU;AAAA,IAAO,OAAO,GAAG,KAAK,MAAM,UAAU,EAAE;AAAA,EACtD,IAAI,UAAU;AAAA,IAAS,OAAO,IAAI,UAAU,MAAO,QAAQ,CAAC;AAAA,EAC5D,OAAO,IAAI,UAAU,OAAQ,QAAQ,CAAC;AAAA;AAGhC,SAAS,sBAAsB,CACrC,SACmB;AAAA,EACnB,MAAM,aAAa,QAAQ,cAAc;AAAA,EACzC,MAAM,MAAM,QAAQ,QAAQ,MAAM,KAAK,IAAI;AAAA,EAC3C,MAAM,QACL,QAAQ,UAAU,CAAC,SAAiB,QAAQ,OAAO,MAAM,GAAG;AAAA,CAAQ;AAAA,EAErE,MAAM,YAAY,IAAI;AAAA,EACtB,IAAI,WAAW;AAAA,EAEf,SAAS,IAAI,CAAC,WAAmB,QAAyB;AAAA,IACzD,MAAM,WAAW,IAAI,IAAI,aAAa;AAAA,IACtC,MAAM,QAAQ,CAAC,GAAG,QAAQ,QAAQ;AAAA,IAElC,IAAI,QAAQ,UAAU,aAAa,QAAQ,QAAQ,GAAG;AAAA,MACrD,MAAM,MAAM,KAAK,MAAO,YAAY,QAAQ,QAAS,GAAG;AAAA,MACxD,MAAM,KAAK,GAAG,aAAa,QAAQ,UAAU,OAAO;AAAA,MAGpD,IAAI,YAAY,KAAK,UAAU,GAAG;AAAA,QACjC,MAAM,aAAc,QAAQ,QAAQ,aAAa,YAAa;AAAA,QAC9D,MAAM,KAAK,OAAO,eAAe,SAAS,GAAG;AAAA,MAC9C;AAAA,IACD,EAAO;AAAA,MACN,MAAM,KAAK,OAAO,SAAS,CAAC;AAAA;AAAA,IAG7B,MAAM,KAAK,WAAW,eAAe,OAAO,GAAG;AAAA,IAC/C,IAAI;AAAA,MAAQ,MAAM,KAAK,KAAK,QAAQ;AAAA,IACpC,OAAO,KAAK,MAAM,KAAK,GAAG;AAAA;AAAA,EAG3B,OAAO;AAAA,IACN,IAAI,CAAC,WAAW,QAAQ;AAAA,MACvB,MAAM,UAAU,IAAI;AAAA,MACpB,IAAI,UAAU,WAAW;AAAA,QAAY;AAAA,MACrC,WAAW;AAAA,MACX,MAAM,KAAK,WAAW,MAAM,CAAC;AAAA;AAAA,IAE9B,MAAM,CAAC,QAAQ;AAAA,MACd,WAAW,IAAI;AAAA,MACf,MAAM,KAAK,QAAQ,SAAS,GAAG,MAAM,CAAC;AAAA;AAAA,EAExC;AAAA;",
|
|
8
|
+
"debugId": "65F27E8CDC95C42364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Kysely } from "kysely";
|
|
1
2
|
import { ColumnType, Generated } from "kysely";
|
|
2
3
|
/**
|
|
3
4
|
* Coverage-kernel constraints. SQL CHECKs in migration 0116 must stay
|
|
@@ -152,7 +153,6 @@ interface SubgraphsTable {
|
|
|
152
153
|
handler_code: string | null;
|
|
153
154
|
source_code: string | null;
|
|
154
155
|
project_id: string | null;
|
|
155
|
-
visibility: Generated<string>;
|
|
156
156
|
/** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
|
|
157
157
|
expires_at: Date | null;
|
|
158
158
|
/** (event type, contract) probe pairs persisted at deploy for weight classification. */
|
|
@@ -935,7 +935,6 @@ interface WaitlistSignupsTable {
|
|
|
935
935
|
answers: Record<string, unknown>;
|
|
936
936
|
created_at: Generated<Date>;
|
|
937
937
|
}
|
|
938
|
-
import { Kysely } from "kysely";
|
|
939
938
|
/**
|
|
940
939
|
* Cheap, SQL-computed digests over a height range — the basis of a verify that
|
|
941
940
|
* runs in seconds instead of hours.
|
|
@@ -997,4 +996,4 @@ type RangeComparison = {
|
|
|
997
996
|
* fork-point corruption signature.
|
|
998
997
|
*/
|
|
999
998
|
declare function compareRangeDigests(local: readonly RangeDigest[], reference: readonly RangeDigest[]): RangeComparison[];
|
|
1000
|
-
export {
|
|
999
|
+
export { RANGE_DIGEST_SPEC, RangeComparison, RangeDigest, RangeDigestDataset, compareRangeDigests, computeRangeDigest, computeRangeDigests };
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
1
|
// src/archive/range-digest.ts
|
|
18
2
|
import { sql } from "kysely";
|
|
19
3
|
var RANGE_DIGEST_SPEC = "md5:sql-identity-v1";
|
|
@@ -132,11 +116,11 @@ function compareRangeDigests(local, reference) {
|
|
|
132
116
|
});
|
|
133
117
|
}
|
|
134
118
|
export {
|
|
135
|
-
|
|
136
|
-
computeRangeDigest,
|
|
119
|
+
RANGE_DIGEST_SPEC,
|
|
137
120
|
compareRangeDigests,
|
|
138
|
-
|
|
121
|
+
computeRangeDigest,
|
|
122
|
+
computeRangeDigests
|
|
139
123
|
};
|
|
140
124
|
|
|
141
|
-
//# debugId=
|
|
125
|
+
//# debugId=C7028A616930DDAA64756E2164756E21
|
|
142
126
|
//# sourceMappingURL=range-digest.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import { sql } from \"kysely\";\nimport type { Kysely } from \"kysely\";\nimport type { Database } from \"../db/schema.ts\";\n\n/**\n * Cheap, SQL-computed digests over a height range — the basis of a verify that\n * runs in seconds instead of hours.\n *\n * The archive's per-object `sha256:parquet-object` digests can only be checked\n * by regenerating the Parquet locally, which costs a full export. That is a\n * reasonable paranoid check and a terrible first-run experience: nobody will\n * spend two hours to discover whether they are broken. These digests are\n * computed by the database itself, over the columns that actually carry chain\n * identity, so an operator gets an answer before they lose interest.\n *\n * What this catches is not a subset chosen for convenience — it is precisely\n * the defect class seen in production: missing heights, a canonical child whose\n * `parent_hash` names a block we no longer hold, duplicate canonical rows at one\n * height, and any altered block/transaction identity. What it deliberately does\n * NOT catch is a difference in bulky payload columns (`raw_tx`, event `data`)\n * that leaves identity intact; `--deep` re-export exists for that.\n *\n * Determinism rules, so two independent databases agree:\n * - Aggregate over an explicit ORDER BY, never table order.\n * - Hash each row to a fixed-width digest FIRST, then aggregate those, so the\n * intermediate string is 32 bytes/row rather than the whole row.\n * - Normalize NULL to a sentinel that cannot collide with a real value.\n * - Never include a column whose representation is environment-dependent\n * (timestamps with timezone rendering, generated ids, `created_at`).\n */\n\nexport const RANGE_DIGEST_SPEC = \"md5:sql-identity-v1\" as const;\n\nexport type RangeDigestDataset = \"blocks\" | \"transactions\" | \"events\";\n\nexport type RangeDigest = {\n\tdataset: RangeDigestDataset;\n\tfrom_block: number;\n\tto_block: number;\n\trow_count: number;\n\t/** null when the range holds no rows — an empty range has no digest, and\n\t * that is materially different from \"a digest that happens to be of zero\n\t * rows\", which would compare equal across datasets. */\n\tdigest: string | null;\n\tdigest_spec: typeof RANGE_DIGEST_SPEC;\n};\n\n/**\n * Identity columns per dataset. `blocks` carries the whole corruption surface\n * we care about, which is why quick mode leans on it.\n */\nfunction identityExpression(dataset: RangeDigestDataset) {\n\tif (dataset === \"blocks\") {\n\t\treturn sql`md5(\n\t\t\theight::text || '|' ||\n\t\t\thash || '|' ||\n\t\t\tparent_hash || '|' ||\n\t\t\tburn_block_height::text || '|' ||\n\t\t\tcoalesce(burn_block_hash, '~') || '|' ||\n\t\t\tcoalesce(index_block_hash, '~')\n\t\t)`;\n\t}\n\tif (dataset === \"transactions\") {\n\t\treturn sql`md5(\n\t\t\ttx_id || '|' ||\n\t\t\tblock_height::text || '|' ||\n\t\t\ttx_index::text || '|' ||\n\t\t\ttype || '|' ||\n\t\t\tsender || '|' ||\n\t\t\tstatus || '|' ||\n\t\t\tcoalesce(contract_id, '~') || '|' ||\n\t\t\tcoalesce(function_name, '~')\n\t\t)`;\n\t}\n\treturn sql`md5(\n\t\ttx_id || '|' ||\n\t\tblock_height::text || '|' ||\n\t\tevent_index::text || '|' ||\n\t\ttype\n\t)`;\n}\n\nfunction orderExpression(dataset: RangeDigestDataset) {\n\tif (dataset === \"blocks\") return sql`height`;\n\tif (dataset === \"transactions\") return sql`block_height, tx_index, tx_id`;\n\treturn sql`block_height, event_index, tx_id`;\n}\n\n/**\n * Rows are restricted to CANONICAL blocks in every dataset — a non-canonical\n * row at a height must not change the digest, or a database that merely retains\n * more reorg history than another would report as diverging.\n */\nfunction fromExpression(dataset: RangeDigestDataset) {\n\tif (dataset === \"blocks\") {\n\t\treturn sql`FROM blocks WHERE canonical = true`;\n\t}\n\tconst table = dataset === \"transactions\" ? sql`transactions` : sql`events`;\n\treturn sql`FROM ${table} AS child\n\t\tJOIN blocks AS b ON b.height = child.block_height AND b.canonical = true\n\t\tWHERE true`;\n}\n\nfunction heightColumn(dataset: RangeDigestDataset) {\n\treturn dataset === \"blocks\" ? sql`height` : sql`child.block_height`;\n}\n\nexport async function computeRangeDigest(\n\tdb: Kysely<Database>,\n\tdataset: RangeDigestDataset,\n\tfromBlock: number,\n\ttoBlock: number,\n): Promise<RangeDigest> {\n\tconst { rows } = await sql<{ digest: string | null; row_count: string }>`\n\t\tSELECT\n\t\t\tmd5(string_agg(row_digest, '' ORDER BY ord)) AS digest,\n\t\t\tCOUNT(*)::text AS row_count\n\t\tFROM (\n\t\t\tSELECT\n\t\t\t\t${identityExpression(dataset)} AS row_digest,\n\t\t\t\trow_number() OVER (ORDER BY ${orderExpression(dataset)}) AS ord\n\t\t\t${fromExpression(dataset)}\n\t\t\t\tAND ${heightColumn(dataset)} >= ${fromBlock}\n\t\t\t\tAND ${heightColumn(dataset)} <= ${toBlock}\n\t\t) AS identity_rows\n\t`.execute(db);\n\n\tconst row = rows[0];\n\tconst rowCount = Number(row?.row_count ?? 0);\n\treturn {\n\t\tdataset,\n\t\tfrom_block: fromBlock,\n\t\tto_block: toBlock,\n\t\trow_count: rowCount,\n\t\tdigest: rowCount === 0 ? null : (row?.digest ?? null),\n\t\tdigest_spec: RANGE_DIGEST_SPEC,\n\t};\n}\n\nexport async function computeRangeDigests(\n\tdb: Kysely<Database>,\n\tfromBlock: number,\n\ttoBlock: number,\n\tdatasets: readonly RangeDigestDataset[] = [\n\t\t\"blocks\",\n\t\t\"transactions\",\n\t\t\"events\",\n\t],\n): Promise<RangeDigest[]> {\n\tconst digests: RangeDigest[] = [];\n\tfor (const dataset of datasets) {\n\t\tdigests.push(await computeRangeDigest(db, dataset, fromBlock, toBlock));\n\t}\n\treturn digests;\n}\n\nexport type RangeComparison = {\n\tdataset: RangeDigestDataset;\n\tfrom_block: number;\n\tto_block: number;\n\tstatus: \"match\" | \"digest-mismatch\" | \"count-mismatch\" | \"missing-locally\";\n\texpected_digest: string | null;\n\tactual_digest: string | null;\n\texpected_rows: number;\n\tactual_rows: number;\n};\n\n/**\n * Compare locally computed digests against a reference set.\n *\n * Row count is reported separately from digest because the two failures mean\n * different things to an operator: a count difference is missing or extra data\n * (usually a gap or an un-swept reorg), while an equal count with a different\n * digest means the same number of rows carry different identities — the\n * fork-point corruption signature.\n */\nexport function compareRangeDigests(\n\tlocal: readonly RangeDigest[],\n\treference: readonly RangeDigest[],\n): RangeComparison[] {\n\tconst localByKey = new Map(\n\t\tlocal.map((d) => [`${d.dataset}:${d.from_block}-${d.to_block}`, d]),\n\t);\n\n\treturn reference.map((expected) => {\n\t\tconst key = `${expected.dataset}:${expected.from_block}-${expected.to_block}`;\n\t\tconst actual = localByKey.get(key);\n\t\tconst base = {\n\t\t\tdataset: expected.dataset,\n\t\t\tfrom_block: expected.from_block,\n\t\t\tto_block: expected.to_block,\n\t\t\texpected_digest: expected.digest,\n\t\t\texpected_rows: expected.row_count,\n\t\t};\n\t\tif (!actual) {\n\t\t\treturn {\n\t\t\t\t...base,\n\t\t\t\tstatus: \"missing-locally\" as const,\n\t\t\t\tactual_digest: null,\n\t\t\t\tactual_rows: 0,\n\t\t\t};\n\t\t}\n\t\tconst status =\n\t\t\tactual.row_count !== expected.row_count\n\t\t\t\t? (\"count-mismatch\" as const)\n\t\t\t\t: actual.digest === expected.digest\n\t\t\t\t\t? (\"match\" as const)\n\t\t\t\t\t: (\"digest-mismatch\" as const);\n\t\treturn {\n\t\t\t...base,\n\t\t\tstatus,\n\t\t\tactual_digest: actual.digest,\n\t\t\tactual_rows: actual.row_count,\n\t\t};\n\t});\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAAA;AA+BO,IAAM,oBAAoB;AAoBjC,SAAS,kBAAkB,CAAC,SAA6B;AAAA,EACxD,IAAI,YAAY,UAAU;AAAA,IACzB,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQR;AAAA,EACA,IAAI,YAAY,gBAAgB;AAAA,IAC/B,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUR;AAAA,EACA,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQR,SAAS,eAAe,CAAC,SAA6B;AAAA,EACrD,IAAI,YAAY;AAAA,IAAU,OAAO;AAAA,EACjC,IAAI,YAAY;AAAA,IAAgB,OAAO;AAAA,EACvC,OAAO;AAAA;AAQR,SAAS,cAAc,CAAC,SAA6B;AAAA,EACpD,IAAI,YAAY,UAAU;AAAA,IACzB,OAAO;AAAA,EACR;AAAA,EACA,MAAM,QAAQ,YAAY,iBAAiB,oBAAoB;AAAA,EAC/D,OAAO,WAAW;AAAA;AAAA;AAAA;AAKnB,SAAS,YAAY,CAAC,SAA6B;AAAA,EAClD,OAAO,YAAY,WAAW,cAAc;AAAA;AAG7C,eAAsB,kBAAkB,CACvC,IACA,SACA,WACA,SACuB;AAAA,EACvB,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAMlB,mBAAmB,OAAO;AAAA,kCACE,gBAAgB,OAAO;AAAA,KACpD,eAAe,OAAO;AAAA,UACjB,aAAa,OAAO,QAAQ;AAAA,UAC5B,aAAa,OAAO,QAAQ;AAAA;AAAA,GAEnC,QAAQ,EAAE;AAAA,EAEZ,MAAM,MAAM,KAAK;AAAA,EACjB,MAAM,WAAW,OAAO,KAAK,aAAa,CAAC;AAAA,EAC3C,OAAO;AAAA,IACN;AAAA,IACA,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ,aAAa,IAAI,OAAQ,KAAK,UAAU;AAAA,IAChD,aAAa;AAAA,EACd;AAAA;AAGD,eAAsB,mBAAmB,CACxC,IACA,WACA,SACA,WAA0C;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACD,GACyB;AAAA,EACzB,MAAM,UAAyB,CAAC;AAAA,EAChC,WAAW,WAAW,UAAU;AAAA,IAC/B,QAAQ,KAAK,MAAM,mBAAmB,IAAI,SAAS,WAAW,OAAO,CAAC;AAAA,EACvE;AAAA,EACA,OAAO;AAAA;AAuBD,SAAS,mBAAmB,CAClC,OACA,WACoB;AAAA,EACpB,MAAM,aAAa,IAAI,IACtB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,CAAC,CAAC,CACnE;AAAA,EAEA,OAAO,UAAU,IAAI,CAAC,aAAa;AAAA,IAClC,MAAM,MAAM,GAAG,SAAS,WAAW,SAAS,cAAc,SAAS;AAAA,IACnE,MAAM,SAAS,WAAW,IAAI,GAAG;AAAA,IACjC,MAAM,OAAO;AAAA,MACZ,SAAS,SAAS;AAAA,MAClB,YAAY,SAAS;AAAA,MACrB,UAAU,SAAS;AAAA,MACnB,iBAAiB,SAAS;AAAA,MAC1B,eAAe,SAAS;AAAA,IACzB;AAAA,IACA,IAAI,CAAC,QAAQ;AAAA,MACZ,OAAO;AAAA,WACH;AAAA,QACH,QAAQ;AAAA,QACR,eAAe;AAAA,QACf,aAAa;AAAA,MACd;AAAA,IACD;AAAA,IACA,MAAM,SACL,OAAO,cAAc,SAAS,YAC1B,mBACD,OAAO,WAAW,SAAS,SACzB,UACA;AAAA,IACN,OAAO;AAAA,SACH;AAAA,MACH;AAAA,MACA,eAAe,OAAO;AAAA,MACtB,aAAa,OAAO;AAAA,IACrB;AAAA,GACA;AAAA;",
|
|
8
|
+
"debugId": "C7028A616930DDAA64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -167,4 +167,4 @@ declare function resolveArchivePublicKey(input: {
|
|
|
167
167
|
/** Test seam for the hosted endpoint; production callers leave it unset. */
|
|
168
168
|
hostedKeyUrl?: string
|
|
169
169
|
}): Promise<string>;
|
|
170
|
-
export {
|
|
170
|
+
export { ArchiveManifest, ArchivePartition, LoadReferenceOptions, LoadedReference, SignatureResult, checkSignature, loadReference, resolveArchivePublicKey };
|
|
@@ -14,16 +14,21 @@ var __export = (target, all) => {
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
// src/archive/reference.ts
|
|
18
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
19
|
+
import { readFile } from "node:fs/promises";
|
|
20
|
+
import { dirname, join, resolve } from "node:path";
|
|
21
|
+
|
|
17
22
|
// src/crypto/ed25519.ts
|
|
18
23
|
var exports_ed25519 = {};
|
|
19
24
|
__export(exports_ed25519, {
|
|
20
|
-
|
|
21
|
-
signEd25519: () => signEd25519,
|
|
22
|
-
publicKeyPemFromPrivate: () => publicKeyPemFromPrivate,
|
|
23
|
-
loadEd25519PublicKey: () => loadEd25519PublicKey,
|
|
24
|
-
loadEd25519PrivateKey: () => loadEd25519PrivateKey,
|
|
25
|
+
ed25519KeyId: () => ed25519KeyId,
|
|
25
26
|
generateEd25519KeyPair: () => generateEd25519KeyPair,
|
|
26
|
-
|
|
27
|
+
loadEd25519PrivateKey: () => loadEd25519PrivateKey,
|
|
28
|
+
loadEd25519PublicKey: () => loadEd25519PublicKey,
|
|
29
|
+
publicKeyPemFromPrivate: () => publicKeyPemFromPrivate,
|
|
30
|
+
signEd25519: () => signEd25519,
|
|
31
|
+
verifyEd25519: () => verifyEd25519
|
|
27
32
|
});
|
|
28
33
|
import {
|
|
29
34
|
createHash,
|
|
@@ -106,9 +111,6 @@ var ARCHIVE_ROOT_PUBLIC_KEY_PEM = [
|
|
|
106
111
|
var ARCHIVE_ROOT_KEY_ID = "fHQWzs9ML2WIYakf";
|
|
107
112
|
|
|
108
113
|
// src/archive/reference.ts
|
|
109
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
110
|
-
import { readFile } from "node:fs/promises";
|
|
111
|
-
import { dirname, join, resolve } from "node:path";
|
|
112
114
|
function isLatestPointer(value) {
|
|
113
115
|
const pointer = value;
|
|
114
116
|
return typeof pointer.snapshot_path === "string" && (value.partitions === undefined || value.partitions.length === 0);
|
|
@@ -222,10 +224,10 @@ async function resolveArchivePublicKey(input) {
|
|
|
222
224
|
return ARCHIVE_ROOT_PUBLIC_KEY_PEM;
|
|
223
225
|
}
|
|
224
226
|
export {
|
|
225
|
-
|
|
227
|
+
checkSignature,
|
|
226
228
|
loadReference,
|
|
227
|
-
|
|
229
|
+
resolveArchivePublicKey
|
|
228
230
|
};
|
|
229
231
|
|
|
230
|
-
//# debugId=
|
|
232
|
+
//# debugId=001D083D88CAE76364756E2164756E21
|
|
231
233
|
//# sourceMappingURL=reference.js.map
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/crypto/ed25519.ts", "../src/streams-bulk-manifest.ts", "../src/archive/root-key.ts"
|
|
3
|
+
"sources": ["../src/archive/reference.ts", "../src/crypto/ed25519.ts", "../src/streams-bulk-manifest.ts", "../src/archive/root-key.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
+
"import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { verifyStreamsBulkManifestSignature } from \"../streams-bulk-manifest.ts\";\nimport type { RangeDigest } from \"./range-digest.ts\";\nimport { ARCHIVE_ROOT_PUBLIC_KEY_PEM } from \"./root-key.ts\";\nimport type { PartitionSemanticDigest } from \"./semantic-digest.ts\";\n\n/**\n * Loading and trusting an archive reference — shared by the CLI and the SDK\n * so the two cannot disagree about what \"the archive says\".\n *\n * A reference is either an https URL or a local path to a snapshot manifest.\n * The SDK only passes `http(s)://` sources; local paths stay CLI. Partition\n * objects live beside the snapshot at `<root>/<dataset>/<name>.parquet`,\n * where the root is two levels above the manifest\n * (`<root>/snapshots/<digest>.json`).\n */\n\nexport type ArchivePartition = {\n\tdataset: string;\n\tfrom_block: number;\n\tto_block: number;\n\tpath: string;\n\trow_count: number;\n\tbyte_size: number;\n\tsha256: string;\n};\n\nexport type ArchiveManifest = {\n\tnetwork?: string;\n\tcoverage?: { from_block: number; to_block: number };\n\tpartition_size_blocks?: number;\n\trange_digests?: RangeDigest[];\n\tpartition_semantic_digests?: PartitionSemanticDigest[];\n\tpartitions?: ArchivePartition[];\n\tsignature?: string;\n\tkey_id?: string;\n\t[key: string]: unknown;\n};\n\nexport type LoadedReference = {\n\tmanifest: ArchiveManifest;\n\torigin: string;\n\t/** Base for resolving partition paths; a URL prefix or a directory. */\n\troot: string;\n\tisRemote: boolean;\n};\n\n/**\n * The archive root, `latest.json`, is a signed POINTER at a snapshot manifest,\n * not a manifest itself. It is also the only URL a user can be expected to\n * know, so every command accepts it and follows it — anything else makes the\n * obvious command fail with an error about digests, which reads as \"this tool\n * is broken\" rather than \"you passed the wrong file\".\n */\ntype LatestPointer = {\n\tsnapshot_path?: string;\n\tsnapshot_digest?: string;\n\tsignature?: string;\n\tkey_id?: string;\n};\n\nfunction isLatestPointer(value: ArchiveManifest): boolean {\n\tconst pointer = value as LatestPointer;\n\treturn (\n\t\ttypeof pointer.snapshot_path === \"string\" &&\n\t\t(value.partitions === undefined || value.partitions.length === 0)\n\t);\n}\n\n/** The publisher names snapshots by their content digest and nothing else\n * (`packages/indexer/src/archive/promote-snapshot.ts`). Anything looser is\n * a pointer someone rewrote, and it must not steer a fetch. */\nconst SNAPSHOT_PATH_PATTERN = /^snapshots\\/[0-9a-f]{64}\\.json$/;\n\nasync function fetchJson(\n\turl: string,\n\tfetchImpl: (\n\t\tinput: string | URL | Request,\n\t\tinit?: RequestInit,\n\t) => Promise<Response>,\n): Promise<unknown> {\n\tconst response = await fetchImpl(url, {\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`could not fetch manifest (${response.status} ${response.statusText})`,\n\t\t);\n\t}\n\treturn response.json();\n}\n\nexport type LoadReferenceOptions = {\n\t/** Key used to verify a signed `latest.json` pointer. The pointer names\n\t * which snapshot is current, so without this an attacker holding only\n\t * bucket write access could roll every reader back to an older, still\n\t * validly signed, snapshot. */\n\tpublicKeyPem?: string;\n\t/** Test/SDK seam; production CLI leaves this unset and uses global fetch. */\n\tfetchImpl?: (\n\t\tinput: string | URL | Request,\n\t\tinit?: RequestInit,\n\t) => Promise<Response>;\n};\n\nexport async function loadReference(\n\tsource: string,\n\toptions: LoadReferenceOptions = {},\n): Promise<LoadedReference> {\n\tconst isRemote = /^https?:\\/\\//.test(source);\n\tconst fetchImpl = options.fetchImpl ?? globalThis.fetch;\n\tlet manifest: ArchiveManifest;\n\tlet root: string;\n\n\tif (isRemote) {\n\t\tmanifest = (await fetchJson(source, fetchImpl)) as ArchiveManifest;\n\t\tconst url = new URL(source);\n\t\t// Both `<root>/latest.json` and `<root>/snapshots/<digest>.json` resolve\n\t\t// their partition paths against `<root>`.\n\t\turl.pathname = url.pathname\n\t\t\t.replace(/\\/snapshots\\/[^/]+$/, \"\")\n\t\t\t.replace(/\\/[^/]+\\.json$/, \"\");\n\t\troot = url.toString();\n\t} else {\n\t\tconst path = resolve(source);\n\t\tmanifest = JSON.parse(await readFile(path, \"utf8\")) as ArchiveManifest;\n\t\troot = isLatestPointer(manifest) ? dirname(path) : dirname(dirname(path));\n\t}\n\n\tif (!isLatestPointer(manifest)) {\n\t\treturn { manifest, origin: source, root, isRemote };\n\t}\n\n\t// Follow the pointer, then prove it led where it claimed. A tampered\n\t// pointer could otherwise redirect to a different — and still validly\n\t// signed — snapshot, which is a downgrade attack rather than a forgery.\n\tconst pointer = manifest as ArchiveManifest & LatestPointer;\n\tconst snapshotPath = pointer.snapshot_path as string;\n\tif (!SNAPSHOT_PATH_PATTERN.test(snapshotPath)) {\n\t\tthrow new Error(\n\t\t\t`pointer names an invalid snapshot path: ${snapshotPath} (expected snapshots/<sha256>.json)`,\n\t\t);\n\t}\n\tif (\n\t\ttypeof pointer.snapshot_digest !== \"string\" ||\n\t\t!/^[0-9a-f]{64}$/.test(pointer.snapshot_digest)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"pointer carries no snapshot_digest, so it cannot prove which snapshot is current\",\n\t\t);\n\t}\n\tif (pointer.signature !== undefined) {\n\t\tif (!options.publicKeyPem) {\n\t\t\tthrow new Error(\n\t\t\t\t\"pointer is signed but no public key is available to verify it\",\n\t\t\t);\n\t\t}\n\t\tlet verified = false;\n\t\ttry {\n\t\t\tverified = verifyStreamsBulkManifestSignature(\n\t\t\t\tpointer as Record<string, unknown>,\n\t\t\t\toptions.publicKeyPem,\n\t\t\t);\n\t\t} catch {\n\t\t\tverified = false;\n\t\t}\n\t\tif (!verified) {\n\t\t\tthrow new Error(\n\t\t\t\t\"pointer signature did not verify against the archive key\",\n\t\t\t);\n\t\t}\n\t}\n\n\tconst resolved = isRemote\n\t\t? ((await fetchJson(\n\t\t\t\t`${root.replace(/\\/$/, \"\")}/${snapshotPath}`,\n\t\t\t\tfetchImpl,\n\t\t\t)) as ArchiveManifest)\n\t\t: (JSON.parse(\n\t\t\t\tawait readFile(join(root, snapshotPath), \"utf8\"),\n\t\t\t) as ArchiveManifest);\n\n\tconst digest = createHash(\"sha256\")\n\t\t.update(\n\t\t\tJSON.stringify(\n\t\t\t\t(({ signature: _s, key_id: _k, ...rest }) => rest)(\n\t\t\t\t\tresolved as Record<string, unknown>,\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.digest(\"hex\");\n\tif (digest !== pointer.snapshot_digest) {\n\t\tthrow new Error(\n\t\t\t`pointer/snapshot mismatch: latest.json names ${pointer.snapshot_digest}, resolved manifest hashes to ${digest}`,\n\t\t);\n\t}\n\n\treturn { manifest: resolved, origin: source, root, isRemote };\n}\n\nexport type SignatureResult = { verified: boolean; reason?: string };\n\n/**\n * An unsigned or unverifiable reference is worthless as a source of truth:\n * comparing against it yields a confident answer with nothing behind it, and\n * repairing from it would write unverified data into a live database.\n */\nexport function checkSignature(\n\tmanifest: ArchiveManifest,\n\tpublicKeyPem: string | undefined,\n\tinsecure: boolean,\n): SignatureResult {\n\tif (insecure) return { verified: false, reason: \"signature check skipped\" };\n\tif (!manifest.signature) {\n\t\treturn { verified: false, reason: \"manifest carries no signature\" };\n\t}\n\tif (!publicKeyPem) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\treason: \"no public key available to verify with\",\n\t\t};\n\t}\n\ttry {\n\t\tconst verified = verifyStreamsBulkManifestSignature(\n\t\t\tmanifest as Record<string, unknown>,\n\t\t\tpublicKeyPem,\n\t\t);\n\t\treturn {\n\t\t\tverified,\n\t\t\treason: verified ? undefined : \"signature did not verify\",\n\t\t};\n\t} catch (err) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\treason: err instanceof Error ? err.message : \"signature check failed\",\n\t\t};\n\t}\n}\n\nconst HOSTED_SIGNING_KEY_URL =\n\t\"https://api.secondlayer.tools/public/streams/signing-key\";\n\nasync function fetchHostedKey(url: string): Promise<string | undefined> {\n\t// A key fetched over plaintext is whoever sits on the wire, not a key.\n\tif (!url.startsWith(\"https://\")) return undefined;\n\ttry {\n\t\tconst response = await fetch(url, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) return undefined;\n\t\tconst body = (await response.json()) as { public_key_pem?: string };\n\t\treturn typeof body.public_key_pem === \"string\"\n\t\t\t? body.public_key_pem\n\t\t\t: undefined;\n\t} catch {\n\t\t// Offline is a legitimate state; the compiled root key still applies.\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Which key a manifest is checked against, in trust order: an explicit\n * `--public-key`, then `ARCHIVE_SIGNING_PUBLIC_KEY` / `STREAMS_SIGNING_PUBLIC_KEY`\n * from the environment, then the hosted key endpoint over https (never in OSS\n * mode, which must work with no path to api.secondlayer.tools), and finally the\n * key compiled into this release. The compiled key means a fresh self-hosted\n * instance can bootstrap offline; the tradeoff is that rotating it is a CLI\n * release. Plaintext `http://` key sources are never consulted.\n */\nexport async function resolveArchivePublicKey(input: {\n\texplicitPem?: string;\n\tenvPem?: string;\n\tallowHostedApi?: boolean;\n\t/** Test seam for the hosted endpoint; production callers leave it unset. */\n\thostedKeyUrl?: string;\n}): Promise<string> {\n\tif (input.explicitPem) return input.explicitPem;\n\tif (input.envPem) return input.envPem;\n\tif (input.allowHostedApi) {\n\t\tconst hosted = await fetchHostedKey(\n\t\t\tinput.hostedKeyUrl ?? HOSTED_SIGNING_KEY_URL,\n\t\t);\n\t\tif (hosted) return hosted;\n\t}\n\treturn ARCHIVE_ROOT_PUBLIC_KEY_PEM;\n}\n",
|
|
5
6
|
"import {\n\ttype KeyObject,\n\tcreateHash,\n\tcreatePrivateKey,\n\tcreatePublicKey,\n\tgenerateKeyPairSync,\n\tsign as nodeSign,\n\tverify as nodeVerify,\n} from \"node:crypto\";\n\n/**\n * Asymmetric ed25519 signing for Streams response proofs.\n *\n * Asymmetric (not HMAC) so the proof is real: only the server holds the private\n * key, and any consumer verifies with the published public key — no shared\n * secret to leak. ed25519 uses node's `sign`/`verify` with a `null` algorithm.\n * Keys are PEM (PKCS8 private / SPKI public) for env transport; load once and\n * reuse the KeyObject on hot paths.\n */\n\nexport function generateEd25519KeyPair(): {\n\tprivateKeyPem: string;\n\tpublicKeyPem: string;\n} {\n\tconst { privateKey, publicKey } = generateKeyPairSync(\"ed25519\");\n\treturn {\n\t\tprivateKeyPem: privateKey\n\t\t\t.export({ format: \"pem\", type: \"pkcs8\" })\n\t\t\t.toString(),\n\t\tpublicKeyPem: publicKey.export({ format: \"pem\", type: \"spki\" }).toString(),\n\t};\n}\n\nexport function loadEd25519PrivateKey(pem: string): KeyObject {\n\treturn createPrivateKey(pem);\n}\n\nexport function loadEd25519PublicKey(pem: string): KeyObject {\n\treturn createPublicKey(pem);\n}\n\nexport function publicKeyPemFromPrivate(privateKeyPem: string): string {\n\treturn createPublicKey(createPrivateKey(privateKeyPem))\n\t\t.export({ format: \"pem\", type: \"spki\" })\n\t\t.toString();\n}\n\n/** Stable short id for a public key (rotation hint via X-Signature-KeyId). */\nexport function ed25519KeyId(publicKeyPem: string): string {\n\tconst der = createPublicKey(publicKeyPem).export({\n\t\tformat: \"der\",\n\t\ttype: \"spki\",\n\t});\n\treturn createHash(\"sha256\").update(der).digest(\"base64url\").slice(0, 16);\n}\n\nexport function signEd25519(payload: string, privateKey: KeyObject): string {\n\treturn nodeSign(null, Buffer.from(payload, \"utf8\"), privateKey).toString(\n\t\t\"base64\",\n\t);\n}\n\nexport function verifyEd25519(\n\tpayload: string,\n\tsignatureBase64: string,\n\tpublicKey: KeyObject,\n): boolean {\n\ttry {\n\t\treturn nodeVerify(\n\t\t\tnull,\n\t\t\tBuffer.from(payload, \"utf8\"),\n\t\t\tpublicKey,\n\t\t\tBuffer.from(signatureBase64, \"base64\"),\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n",
|
|
6
7
|
"import {\n\ted25519KeyId,\n\tloadEd25519PrivateKey,\n\tloadEd25519PublicKey,\n\tpublicKeyPemFromPrivate,\n\tsignEd25519,\n\tverifyEd25519,\n} from \"./crypto/ed25519.ts\";\n\n/**\n * ed25519 signing for the Streams cold-bulk parquet manifest.\n *\n * The live Streams lane is ed25519-signed; the bulk manifest was plain JSON with\n * only per-file sha256, so a tampered manifest+file pair verified cleanly. This\n * signs the manifest itself with the same platform key, so a consumer can trust\n * the file hashes only after the manifest signature checks out — making the two\n * availability lanes symmetric.\n *\n * The signed bytes are the manifest's canonical JSON with the signature envelope\n * fields removed, so signer and verifier agree without a separate canonical\n * form: `signature`/`key_id` are appended last, so stripping them and\n * re-serializing reproduces the exact pre-sign bytes.\n */\ntype SignatureEnvelope = { signature?: string; key_id?: string };\n\n/** The exact bytes a manifest signature covers: the manifest JSON minus the\n * signature envelope fields. */\nexport function canonicalStreamsBulkManifestPayload(\n\tmanifest: Record<string, unknown> & SignatureEnvelope,\n): string {\n\tconst { signature: _signature, key_id: _keyId, ...rest } = manifest;\n\treturn JSON.stringify(rest);\n}\n\nfunction normalizePem(pem: string): string {\n\t// Env transport often escapes newlines; restore real PEM line breaks.\n\treturn pem.includes(\"\\\\n\") ? pem.replace(/\\\\n/g, \"\\n\") : pem;\n}\n\n/**\n * Attach an ed25519 `signature` + `key_id` over the manifest's canonical bytes.\n * Returns a new manifest; re-signing one that already carries a signature signs\n * over its un-enveloped form (idempotent shape).\n */\nexport function signStreamsBulkManifest<\n\tT extends Record<string, unknown> & SignatureEnvelope,\n>(\n\tmanifest: T,\n\tprivateKeyPem: string,\n): T & { signature: string; key_id: string } {\n\tconst pem = normalizePem(privateKeyPem);\n\tconst privateKey = loadEd25519PrivateKey(pem);\n\tconst keyId = ed25519KeyId(publicKeyPemFromPrivate(pem));\n\tconst { signature: _signature, key_id: _keyId, ...base } = manifest;\n\tconst payload = JSON.stringify(base);\n\treturn {\n\t\t...(base as T),\n\t\tsignature: signEd25519(payload, privateKey),\n\t\tkey_id: keyId,\n\t};\n}\n\n/** Verify a manifest's ed25519 signature against the published public key. */\nexport function verifyStreamsBulkManifestSignature(\n\tmanifest: Record<string, unknown> & SignatureEnvelope,\n\tpublicKeyPem: string,\n): boolean {\n\tif (!manifest.signature) return false;\n\tconst payload = canonicalStreamsBulkManifestPayload(manifest);\n\treturn verifyEd25519(\n\t\tpayload,\n\t\tmanifest.signature,\n\t\tloadEd25519PublicKey(publicKeyPem),\n\t);\n}\n",
|
|
7
|
-
"/**\n * The archive signing key, compiled into every release.\n *\n * `secondlayer bootstrap`, `verify`, and `repair` refuse a manifest they\n * cannot verify, so a self-hosted instance with no network path to the hosted\n * API still needs a key it can trust. Shipping it here means the trust root\n * travels with the CLI rather than with whichever host answers\n * `/public/streams/signing-key`.\n *\n * Tradeoff: rotating this key is a CLI release until a root-signed key\n * ceremony lands. Operators who want a different root pin one with\n * `ARCHIVE_SIGNING_PUBLIC_KEY` or `--public-key`, which win over this value.\n *\n * Value: `GET https://api.secondlayer.tools/public/streams/signing-key`,\n * key_id `fHQWzs9ML2WIYakf`, ed25519, fetched 2026-08-27.\n */\nexport const ARCHIVE_ROOT_PUBLIC_KEY_PEM: string = [\n\t\"-----BEGIN PUBLIC KEY-----\",\n\t\"MCowBQYDK2VwAyEAq7wXm235JkFbElDmTVdNtmTKqRLx4PzjBgZhyREUFis=\",\n\t\"-----END PUBLIC KEY-----\",\n\t\"\",\n].join(\"\\n\");\n\nexport const ARCHIVE_ROOT_KEY_ID: string = \"fHQWzs9ML2WIYakf\";\n"
|
|
8
|
-
"import { createHash } from \"node:crypto\";\nimport { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { verifyStreamsBulkManifestSignature } from \"../streams-bulk-manifest.ts\";\nimport type { RangeDigest } from \"./range-digest.ts\";\nimport { ARCHIVE_ROOT_PUBLIC_KEY_PEM } from \"./root-key.ts\";\nimport type { PartitionSemanticDigest } from \"./semantic-digest.ts\";\n\n/**\n * Loading and trusting an archive reference — shared by the CLI and the SDK\n * so the two cannot disagree about what \"the archive says\".\n *\n * A reference is either an https URL or a local path to a snapshot manifest.\n * The SDK only passes `http(s)://` sources; local paths stay CLI. Partition\n * objects live beside the snapshot at `<root>/<dataset>/<name>.parquet`,\n * where the root is two levels above the manifest\n * (`<root>/snapshots/<digest>.json`).\n */\n\nexport type ArchivePartition = {\n\tdataset: string;\n\tfrom_block: number;\n\tto_block: number;\n\tpath: string;\n\trow_count: number;\n\tbyte_size: number;\n\tsha256: string;\n};\n\nexport type ArchiveManifest = {\n\tnetwork?: string;\n\tcoverage?: { from_block: number; to_block: number };\n\tpartition_size_blocks?: number;\n\trange_digests?: RangeDigest[];\n\tpartition_semantic_digests?: PartitionSemanticDigest[];\n\tpartitions?: ArchivePartition[];\n\tsignature?: string;\n\tkey_id?: string;\n\t[key: string]: unknown;\n};\n\nexport type LoadedReference = {\n\tmanifest: ArchiveManifest;\n\torigin: string;\n\t/** Base for resolving partition paths; a URL prefix or a directory. */\n\troot: string;\n\tisRemote: boolean;\n};\n\n/**\n * The archive root, `latest.json`, is a signed POINTER at a snapshot manifest,\n * not a manifest itself. It is also the only URL a user can be expected to\n * know, so every command accepts it and follows it — anything else makes the\n * obvious command fail with an error about digests, which reads as \"this tool\n * is broken\" rather than \"you passed the wrong file\".\n */\ntype LatestPointer = {\n\tsnapshot_path?: string;\n\tsnapshot_digest?: string;\n\tsignature?: string;\n\tkey_id?: string;\n};\n\nfunction isLatestPointer(value: ArchiveManifest): boolean {\n\tconst pointer = value as LatestPointer;\n\treturn (\n\t\ttypeof pointer.snapshot_path === \"string\" &&\n\t\t(value.partitions === undefined || value.partitions.length === 0)\n\t);\n}\n\n/** The publisher names snapshots by their content digest and nothing else\n * (`packages/indexer/src/archive/promote-snapshot.ts`). Anything looser is\n * a pointer someone rewrote, and it must not steer a fetch. */\nconst SNAPSHOT_PATH_PATTERN = /^snapshots\\/[0-9a-f]{64}\\.json$/;\n\nasync function fetchJson(\n\turl: string,\n\tfetchImpl: (\n\t\tinput: string | URL | Request,\n\t\tinit?: RequestInit,\n\t) => Promise<Response>,\n): Promise<unknown> {\n\tconst response = await fetchImpl(url, {\n\t\tsignal: AbortSignal.timeout(30_000),\n\t});\n\tif (!response.ok) {\n\t\tthrow new Error(\n\t\t\t`could not fetch manifest (${response.status} ${response.statusText})`,\n\t\t);\n\t}\n\treturn response.json();\n}\n\nexport type LoadReferenceOptions = {\n\t/** Key used to verify a signed `latest.json` pointer. The pointer names\n\t * which snapshot is current, so without this an attacker holding only\n\t * bucket write access could roll every reader back to an older, still\n\t * validly signed, snapshot. */\n\tpublicKeyPem?: string;\n\t/** Test/SDK seam; production CLI leaves this unset and uses global fetch. */\n\tfetchImpl?: (\n\t\tinput: string | URL | Request,\n\t\tinit?: RequestInit,\n\t) => Promise<Response>;\n};\n\nexport async function loadReference(\n\tsource: string,\n\toptions: LoadReferenceOptions = {},\n): Promise<LoadedReference> {\n\tconst isRemote = /^https?:\\/\\//.test(source);\n\tconst fetchImpl = options.fetchImpl ?? globalThis.fetch;\n\tlet manifest: ArchiveManifest;\n\tlet root: string;\n\n\tif (isRemote) {\n\t\tmanifest = (await fetchJson(source, fetchImpl)) as ArchiveManifest;\n\t\tconst url = new URL(source);\n\t\t// Both `<root>/latest.json` and `<root>/snapshots/<digest>.json` resolve\n\t\t// their partition paths against `<root>`.\n\t\turl.pathname = url.pathname\n\t\t\t.replace(/\\/snapshots\\/[^/]+$/, \"\")\n\t\t\t.replace(/\\/[^/]+\\.json$/, \"\");\n\t\troot = url.toString();\n\t} else {\n\t\tconst path = resolve(source);\n\t\tmanifest = JSON.parse(await readFile(path, \"utf8\")) as ArchiveManifest;\n\t\troot = isLatestPointer(manifest) ? dirname(path) : dirname(dirname(path));\n\t}\n\n\tif (!isLatestPointer(manifest)) {\n\t\treturn { manifest, origin: source, root, isRemote };\n\t}\n\n\t// Follow the pointer, then prove it led where it claimed. A tampered\n\t// pointer could otherwise redirect to a different — and still validly\n\t// signed — snapshot, which is a downgrade attack rather than a forgery.\n\tconst pointer = manifest as ArchiveManifest & LatestPointer;\n\tconst snapshotPath = pointer.snapshot_path as string;\n\tif (!SNAPSHOT_PATH_PATTERN.test(snapshotPath)) {\n\t\tthrow new Error(\n\t\t\t`pointer names an invalid snapshot path: ${snapshotPath} (expected snapshots/<sha256>.json)`,\n\t\t);\n\t}\n\tif (\n\t\ttypeof pointer.snapshot_digest !== \"string\" ||\n\t\t!/^[0-9a-f]{64}$/.test(pointer.snapshot_digest)\n\t) {\n\t\tthrow new Error(\n\t\t\t\"pointer carries no snapshot_digest, so it cannot prove which snapshot is current\",\n\t\t);\n\t}\n\tif (pointer.signature !== undefined) {\n\t\tif (!options.publicKeyPem) {\n\t\t\tthrow new Error(\n\t\t\t\t\"pointer is signed but no public key is available to verify it\",\n\t\t\t);\n\t\t}\n\t\tlet verified = false;\n\t\ttry {\n\t\t\tverified = verifyStreamsBulkManifestSignature(\n\t\t\t\tpointer as Record<string, unknown>,\n\t\t\t\toptions.publicKeyPem,\n\t\t\t);\n\t\t} catch {\n\t\t\tverified = false;\n\t\t}\n\t\tif (!verified) {\n\t\t\tthrow new Error(\n\t\t\t\t\"pointer signature did not verify against the archive key\",\n\t\t\t);\n\t\t}\n\t}\n\n\tconst resolved = isRemote\n\t\t? ((await fetchJson(\n\t\t\t\t`${root.replace(/\\/$/, \"\")}/${snapshotPath}`,\n\t\t\t\tfetchImpl,\n\t\t\t)) as ArchiveManifest)\n\t\t: (JSON.parse(\n\t\t\t\tawait readFile(join(root, snapshotPath), \"utf8\"),\n\t\t\t) as ArchiveManifest);\n\n\tconst digest = createHash(\"sha256\")\n\t\t.update(\n\t\t\tJSON.stringify(\n\t\t\t\t(({ signature: _s, key_id: _k, ...rest }) => rest)(\n\t\t\t\t\tresolved as Record<string, unknown>,\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\t.digest(\"hex\");\n\tif (digest !== pointer.snapshot_digest) {\n\t\tthrow new Error(\n\t\t\t`pointer/snapshot mismatch: latest.json names ${pointer.snapshot_digest}, resolved manifest hashes to ${digest}`,\n\t\t);\n\t}\n\n\treturn { manifest: resolved, origin: source, root, isRemote };\n}\n\nexport type SignatureResult = { verified: boolean; reason?: string };\n\n/**\n * An unsigned or unverifiable reference is worthless as a source of truth:\n * comparing against it yields a confident answer with nothing behind it, and\n * repairing from it would write unverified data into a live database.\n */\nexport function checkSignature(\n\tmanifest: ArchiveManifest,\n\tpublicKeyPem: string | undefined,\n\tinsecure: boolean,\n): SignatureResult {\n\tif (insecure) return { verified: false, reason: \"signature check skipped\" };\n\tif (!manifest.signature) {\n\t\treturn { verified: false, reason: \"manifest carries no signature\" };\n\t}\n\tif (!publicKeyPem) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\treason: \"no public key available to verify with\",\n\t\t};\n\t}\n\ttry {\n\t\tconst verified = verifyStreamsBulkManifestSignature(\n\t\t\tmanifest as Record<string, unknown>,\n\t\t\tpublicKeyPem,\n\t\t);\n\t\treturn {\n\t\t\tverified,\n\t\t\treason: verified ? undefined : \"signature did not verify\",\n\t\t};\n\t} catch (err) {\n\t\treturn {\n\t\t\tverified: false,\n\t\t\treason: err instanceof Error ? err.message : \"signature check failed\",\n\t\t};\n\t}\n}\n\nconst HOSTED_SIGNING_KEY_URL =\n\t\"https://api.secondlayer.tools/public/streams/signing-key\";\n\nasync function fetchHostedKey(url: string): Promise<string | undefined> {\n\t// A key fetched over plaintext is whoever sits on the wire, not a key.\n\tif (!url.startsWith(\"https://\")) return undefined;\n\ttry {\n\t\tconst response = await fetch(url, { signal: AbortSignal.timeout(10_000) });\n\t\tif (!response.ok) return undefined;\n\t\tconst body = (await response.json()) as { public_key_pem?: string };\n\t\treturn typeof body.public_key_pem === \"string\"\n\t\t\t? body.public_key_pem\n\t\t\t: undefined;\n\t} catch {\n\t\t// Offline is a legitimate state; the compiled root key still applies.\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Which key a manifest is checked against, in trust order: an explicit\n * `--public-key`, then `ARCHIVE_SIGNING_PUBLIC_KEY` / `STREAMS_SIGNING_PUBLIC_KEY`\n * from the environment, then the hosted key endpoint over https (never in OSS\n * mode, which must work with no path to api.secondlayer.tools), and finally the\n * key compiled into this release. The compiled key means a fresh self-hosted\n * instance can bootstrap offline; the tradeoff is that rotating it is a CLI\n * release. Plaintext `http://` key sources are never consulted.\n */\nexport async function resolveArchivePublicKey(input: {\n\texplicitPem?: string;\n\tenvPem?: string;\n\tallowHostedApi?: boolean;\n\t/** Test seam for the hosted endpoint; production callers leave it unset. */\n\thostedKeyUrl?: string;\n}): Promise<string> {\n\tif (input.explicitPem) return input.explicitPem;\n\tif (input.envPem) return input.envPem;\n\tif (input.allowHostedApi) {\n\t\tconst hosted = await fetchHostedKey(\n\t\t\tinput.hostedKeyUrl ?? HOSTED_SIGNING_KEY_URL,\n\t\t);\n\t\tif (hosted) return hosted;\n\t}\n\treturn ARCHIVE_ROOT_PUBLIC_KEY_PEM;\n}\n"
|
|
8
|
+
"/**\n * The archive signing key, compiled into every release.\n *\n * `secondlayer bootstrap`, `verify`, and `repair` refuse a manifest they\n * cannot verify, so a self-hosted instance with no network path to the hosted\n * API still needs a key it can trust. Shipping it here means the trust root\n * travels with the CLI rather than with whichever host answers\n * `/public/streams/signing-key`.\n *\n * Tradeoff: rotating this key is a CLI release until a root-signed key\n * ceremony lands. Operators who want a different root pin one with\n * `ARCHIVE_SIGNING_PUBLIC_KEY` or `--public-key`, which win over this value.\n *\n * Value: `GET https://api.secondlayer.tools/public/streams/signing-key`,\n * key_id `fHQWzs9ML2WIYakf`, ed25519, fetched 2026-08-27.\n */\nexport const ARCHIVE_ROOT_PUBLIC_KEY_PEM: string = [\n\t\"-----BEGIN PUBLIC KEY-----\",\n\t\"MCowBQYDK2VwAyEAq7wXm235JkFbElDmTVdNtmTKqRLx4PzjBgZhyREUFis=\",\n\t\"-----END PUBLIC KEY-----\",\n\t\"\",\n].join(\"\\n\");\n\nexport const ARCHIVE_ROOT_KEY_ID: string = \"fHQWzs9ML2WIYakf\";\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": "
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "001D083D88CAE76364756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
|
@@ -1,19 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
1
|
// src/archive/root-key.ts
|
|
18
2
|
var ARCHIVE_ROOT_PUBLIC_KEY_PEM = [
|
|
19
3
|
"-----BEGIN PUBLIC KEY-----",
|
|
@@ -24,9 +8,9 @@ var ARCHIVE_ROOT_PUBLIC_KEY_PEM = [
|
|
|
24
8
|
`);
|
|
25
9
|
var ARCHIVE_ROOT_KEY_ID = "fHQWzs9ML2WIYakf";
|
|
26
10
|
export {
|
|
27
|
-
|
|
28
|
-
|
|
11
|
+
ARCHIVE_ROOT_KEY_ID,
|
|
12
|
+
ARCHIVE_ROOT_PUBLIC_KEY_PEM
|
|
29
13
|
};
|
|
30
14
|
|
|
31
|
-
//# debugId=
|
|
15
|
+
//# debugId=164189521A00A2C764756E2164756E21
|
|
32
16
|
//# sourceMappingURL=root-key.js.map
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"/**\n * The archive signing key, compiled into every release.\n *\n * `secondlayer bootstrap`, `verify`, and `repair` refuse a manifest they\n * cannot verify, so a self-hosted instance with no network path to the hosted\n * API still needs a key it can trust. Shipping it here means the trust root\n * travels with the CLI rather than with whichever host answers\n * `/public/streams/signing-key`.\n *\n * Tradeoff: rotating this key is a CLI release until a root-signed key\n * ceremony lands. Operators who want a different root pin one with\n * `ARCHIVE_SIGNING_PUBLIC_KEY` or `--public-key`, which win over this value.\n *\n * Value: `GET https://api.secondlayer.tools/public/streams/signing-key`,\n * key_id `fHQWzs9ML2WIYakf`, ed25519, fetched 2026-08-27.\n */\nexport const ARCHIVE_ROOT_PUBLIC_KEY_PEM: string = [\n\t\"-----BEGIN PUBLIC KEY-----\",\n\t\"MCowBQYDK2VwAyEAq7wXm235JkFbElDmTVdNtmTKqRLx4PzjBgZhyREUFis=\",\n\t\"-----END PUBLIC KEY-----\",\n\t\"\",\n].join(\"\\n\");\n\nexport const ARCHIVE_ROOT_KEY_ID: string = \"fHQWzs9ML2WIYakf\";\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAgBO,IAAM,8BAAsC;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,EAAE,KAAK;AAAA,CAAI;AAEJ,IAAM,sBAA8B;",
|
|
8
|
+
"debugId": "164189521A00A2C764756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Kysely } from "kysely";
|
|
1
2
|
import { ColumnType, Generated } from "kysely";
|
|
2
3
|
/**
|
|
3
4
|
* Coverage-kernel constraints. SQL CHECKs in migration 0116 must stay
|
|
@@ -152,7 +153,6 @@ interface SubgraphsTable {
|
|
|
152
153
|
handler_code: string | null;
|
|
153
154
|
source_code: string | null;
|
|
154
155
|
project_id: string | null;
|
|
155
|
-
visibility: Generated<string>;
|
|
156
156
|
/** Paid (wallet-ghost) deploys expire unless renewed or claimed; NULL = no expiry. */
|
|
157
157
|
expires_at: Date | null;
|
|
158
158
|
/** (event type, contract) probe pairs persisted at deploy for weight classification. */
|
|
@@ -989,7 +989,6 @@ interface PartitionSemanticDigest {
|
|
|
989
989
|
digest: string | null;
|
|
990
990
|
digest_spec: SemanticDigestSpec;
|
|
991
991
|
}
|
|
992
|
-
import { Kysely } from "kysely";
|
|
993
992
|
/**
|
|
994
993
|
* Compute per-partition semantic-v1 digests directly from a database — the same
|
|
995
994
|
* bytes an exporter would produce, without writing Parquet. Callers: the CLI
|
|
@@ -1022,4 +1021,4 @@ type PartitionSemanticComparison = {
|
|
|
1022
1021
|
actual_rows: number
|
|
1023
1022
|
};
|
|
1024
1023
|
declare function comparePartitionSemanticDigests(local: readonly PartitionSemanticDigest[], reference: readonly PartitionSemanticDigest[]): PartitionSemanticComparison[];
|
|
1025
|
-
export {
|
|
1024
|
+
export { CanonicalDataset, PartitionSemanticComparison, comparePartitionSemanticDigests, computePartitionSemanticDigest };
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
1
|
+
// src/archive/semantic-digest-builder.ts
|
|
2
|
+
import { sql } from "kysely";
|
|
16
3
|
|
|
17
4
|
// src/archive/semantic-digest.ts
|
|
18
5
|
import { createHash } from "node:crypto";
|
|
@@ -134,7 +121,6 @@ class SemanticDigestRollup {
|
|
|
134
121
|
}
|
|
135
122
|
|
|
136
123
|
// src/archive/semantic-digest-builder.ts
|
|
137
|
-
import { sql } from "kysely";
|
|
138
124
|
var DEFAULT_BATCH_ROWS = 20000;
|
|
139
125
|
async function* streamBlockDigests(db, fromBlock, toBlock, batchRows) {
|
|
140
126
|
let last = fromBlock - 1;
|
|
@@ -294,9 +280,9 @@ function comparePartitionSemanticDigests(local, reference) {
|
|
|
294
280
|
});
|
|
295
281
|
}
|
|
296
282
|
export {
|
|
297
|
-
|
|
298
|
-
|
|
283
|
+
comparePartitionSemanticDigests,
|
|
284
|
+
computePartitionSemanticDigest
|
|
299
285
|
};
|
|
300
286
|
|
|
301
|
-
//# debugId=
|
|
287
|
+
//# debugId=0559E7178C9550A164756E2164756E21
|
|
302
288
|
//# sourceMappingURL=semantic-digest-builder.js.map
|