@secondlayer/cli 19.2.1 → 19.3.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/README.md CHANGED
@@ -99,7 +99,7 @@ No account. Writes `.env.local`, restores history, prints the Stacks observer st
99
99
  | `secondlayer bootstrap --against <manifest> [--from-block <n>] [--to-block <n>] [--verify all\|blocks] [--public-key <pem>] [-y] [--json]` | Restore chain history from a verified archive into an empty database. A run that died mid-way resumes per dataset on re-run. After the load, digests for blocks, transactions, and events are checked over the restored range (`--verify blocks` skips the child datasets and the minutes they cost). Partition fetches retry three times with backoff on resets, timeouts, 429 (honoring `Retry-After`) and 5xx; a link that stays down exits `1` with a re-run hint, and the re-run resumes. Exit `0` restored, `1` diverged or interrupted, `2` refused |
100
100
  | `secondlayer observer [--mode indexer\|signer-shared] [--endpoint host:port] [--network …]` | Print the `[[events_observer]]` stanza. Signer-shared skips retries; a missed block stays a gap until `repair` |
101
101
  | `secondlayer verify [all\|raw\|decode:<name>\|subgraph:<name>] --against <manifest> [--quick\|--deep\|--anchor]` | Compare local data to a signed archive. Default target `raw`. Exit `0` clean, `1` diverged, `2` unanchored |
102
- | `secondlayer verify block <height\|index_block_hash> [--node <url>] [--checkpoint <file>] [--rows] [--json]` | Prove one Stacks block from the checkpoint built into this release: Bitcoin headers, burn block, signer set, signatures, state root, state diff, and with `--rows` the indexed rows. Nothing else is trusted; proofs come from the API, or a node with `--node`. Exit `0` proven, `1` a link is broken, `2` could not check |
102
+ | `secondlayer verify block <height\|index_block_hash> [--node <url>] [--checkpoint <file>] [--rows] [--json]` | Prove one Stacks block from the checkpoint built into this release: Bitcoin headers, burn block, signer set, signatures, state root, state diff, and with `--rows` the indexed rows. Blocks below the checkpoint are tied to it by hash (parent links or one MARF proof) instead of signatures. Nothing else is trusted; proofs come from the API, or a node with `--node`. Exit `0` proven, `1` a link is broken, `2` could not check |
103
103
  | `secondlayer repair --against <archive> [--apply] [-y]` | Plan (default) or apply an archive repair. A fixed block is rewritten with its transactions and events from the archive; when the reference has no child partition for a height, the block is rewritten alone, the height is named with a `bootstrap --from-block H --to-block H` remedy, and the exit is `1`. Partition fetches retry like `bootstrap`; a link that stays down exits `1` with a re-run hint, and heights already repaired are kept |
104
104
 
105
105
  Bootstrap and repair against the official hosted archive (`archive.secondlayer.tools`)
package/dist/cli.js CHANGED
@@ -29440,7 +29440,7 @@ var init_git_status = () => {};
29440
29440
 
29441
29441
  // ../../node_modules/typescript/lib/typescript.js
29442
29442
  var require_typescript = __commonJS(function(exports, module) {
29443
- var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-adcb760acc54b4c28/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-adcb760acc54b4c28/node_modules/typescript/lib/typescript.js";
29443
+ var __dirname = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a5aea7deee5fef330/node_modules/typescript/lib", __filename = "/Users/ryan/projects/secondlayer/.claude/worktrees/agent-a5aea7deee5fef330/node_modules/typescript/lib/typescript.js";
29444
29444
  /*! *****************************************************************************
29445
29445
  Copyright (c) Microsoft Corporation. All rights reserved.
29446
29446
  Licensed under the Apache License, Version 2.0 (the "License"); you may not use
@@ -198790,7 +198790,7 @@ import {
198790
198790
  watch,
198791
198791
  writeFileSync as writeFileSync3
198792
198792
  } from "node:fs";
198793
- import { basename, dirname as dirname5, extname, join as join7, resolve as resolve5 } from "node:path";
198793
+ import { basename, dirname as dirname5, extname, join as join7, relative, resolve as resolve5 } from "node:path";
198794
198794
  import { fileURLToPath as fileURLToPath3 } from "node:url";
198795
198795
  import { confirm as confirm2 } from "@inquirer/prompts";
198796
198796
  import {
@@ -198802,6 +198802,10 @@ import {
198802
198802
  billingPausedCode
198803
198803
  } from "@secondlayer/shared/schemas";
198804
198804
  import { TRAIT_STANDARDS } from "@secondlayer/stacks/clarity";
198805
+ import {
198806
+ deriveVerification,
198807
+ isDeterminismViolation
198808
+ } from "@secondlayer/subgraphs/verification";
198805
198809
  function webhookHint(apiUrl, name, firstTable) {
198806
198810
  if (!firstTable || isMerchantUrl(apiUrl))
198807
198811
  return null;
@@ -199121,6 +199125,36 @@ function createSubgraphDeployPreview(def, options = {}) {
199121
199125
  ...options.bundleBytes !== undefined ? { bundleSize: `${options.bundleBytes} bytes` } : {}
199122
199126
  };
199123
199127
  }
199128
+ function formatVerificationLines(verification, pin) {
199129
+ if (!verification)
199130
+ return [];
199131
+ const head = [
199132
+ verification.level,
199133
+ ...pin ? [`pin ${pin.slice(0, 4)}…${pin.slice(-4)}`] : [],
199134
+ ...verification.reasons
199135
+ ].join(" · ");
199136
+ return [
199137
+ `verification ${head}`,
199138
+ ...verification.unproven.map((u) => `unproven ${u}`)
199139
+ ];
199140
+ }
199141
+ function formatVerificationStatus(verification) {
199142
+ if (!verification)
199143
+ return "unknown (deployed before levels were derived)";
199144
+ const head = [verification.level, ...verification.reasons].join(" · ");
199145
+ return verification.unproven.length > 0 ? `${head} (unproven: ${verification.unproven.join("; ")})` : head;
199146
+ }
199147
+ function checkDeterminism(def, findings) {
199148
+ const verification = deriveVerification(def, findings);
199149
+ if (isDeterminismViolation(verification)) {
199150
+ error("Nondeterministic handler. This subgraph's sources are provable, so its handlers must give the same rows on every run:");
199151
+ for (const r of verification.reasons)
199152
+ error(` ✗ ${r}`);
199153
+ info("Derive values from event, ctx.block and ctx.tx only (no Date, Math.random, fetch, timers or locale formatting).");
199154
+ process.exit(1);
199155
+ }
199156
+ return verification;
199157
+ }
199124
199158
  function printSubgraphDeployPreview(preview, context) {
199125
199159
  success("Subgraph deploy dry run passed");
199126
199160
  const pairs = [
@@ -199493,8 +199527,11 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199493
199527
  info(`${dryRun ? "Bundling for remote deploy dry run" : "Bundling for remote deploy"} (${config.network})...`);
199494
199528
  const source = derived?.source ?? await readFile(absPath, "utf8");
199495
199529
  const { bundleSubgraphCode } = await import("@secondlayer/bundler");
199496
- const bundled = await bundleSubgraphCode(source);
199530
+ const bundled = await bundleSubgraphCode(source, {
199531
+ fileName: relative(process.cwd(), absPath)
199532
+ });
199497
199533
  const handlerCode = bundled.handlerCode;
199534
+ const localVerification = checkDeterminism(effectiveDef, bundled.findings);
199498
199535
  if (dryRun) {
199499
199536
  printSubgraphDeployPreview(createSubgraphDeployPreview(validated, {
199500
199537
  bundleBytes: Buffer.byteLength(handlerCode, "utf8")
@@ -199503,6 +199540,9 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199503
199540
  file: absPath,
199504
199541
  bundled: true
199505
199542
  });
199543
+ for (const line of formatVerificationLines(localVerification)) {
199544
+ info(` ${line}`);
199545
+ }
199506
199546
  return;
199507
199547
  }
199508
199548
  const deployStartBlock = startBlock ?? effectiveDef.startBlock;
@@ -199544,6 +199584,11 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199544
199584
  });
199545
199585
  for (const w of result.warnings ?? [])
199546
199586
  warn(w);
199587
+ const printVerification = () => {
199588
+ for (const line of formatVerificationLines(result.verification ?? localVerification, result.pin)) {
199589
+ info(` ${line}`);
199590
+ }
199591
+ };
199547
199592
  const printDeployFooter = async () => {
199548
199593
  try {
199549
199594
  const { apiUrl } = await resolveAuth();
@@ -199563,10 +199608,13 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199563
199608
  };
199564
199609
  if (result.action === "unchanged") {
199565
199610
  info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
199611
+ printVerification();
199566
199612
  } else if (result.action === "handler_updated") {
199567
199613
  success(`Subgraph "${effectiveDef.name}" handler updated (v${result.version} — schema unchanged, no reindex needed)`);
199614
+ printVerification();
199568
199615
  } else if (result.action === "created") {
199569
199616
  success(`Subgraph "${effectiveDef.name}" created → v${result.version}`);
199617
+ printVerification();
199570
199618
  await printDeployFooter();
199571
199619
  } else if (result.action === "reindexed") {
199572
199620
  if (result.diff) {
@@ -199583,6 +199631,7 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199583
199631
  }
199584
199632
  }
199585
199633
  success(`Subgraph "${effectiveDef.name}" updated → v${result.version} (reindexing)`);
199634
+ printVerification();
199586
199635
  await printDeployFooter();
199587
199636
  } else {
199588
199637
  if (result.diff) {
@@ -199594,22 +199643,37 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199594
199643
  }
199595
199644
  }
199596
199645
  success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
199646
+ printVerification();
199597
199647
  await printDeployFooter();
199598
199648
  }
199599
199649
  } else {
199650
+ let findings;
199651
+ try {
199652
+ const { bundleSubgraphCode } = await import("@secondlayer/bundler");
199653
+ findings = (await bundleSubgraphCode(await readFile(absPath, "utf8"), {
199654
+ fileName: relative(process.cwd(), absPath)
199655
+ })).findings;
199656
+ } catch {
199657
+ findings = undefined;
199658
+ }
199659
+ const verification = findings ? checkDeterminism(effectiveDef, findings) : undefined;
199600
199660
  if (dryRun) {
199601
199661
  printSubgraphDeployPreview(createSubgraphDeployPreview(validated), {
199602
199662
  network: config.network,
199603
199663
  file: absPath,
199604
199664
  bundled: false
199605
199665
  });
199666
+ for (const line of formatVerificationLines(verification)) {
199667
+ info(` ${line}`);
199668
+ }
199606
199669
  return;
199607
199670
  }
199608
199671
  const { deploySchema } = await import("@secondlayer/subgraphs");
199609
199672
  const { getDb, closeDb } = await import("@secondlayer/shared/db");
199610
199673
  const db = getDb();
199611
199674
  const result = await deploySchema(db, effectiveDef, absPath, {
199612
- forceReindex: startBlock !== undefined
199675
+ forceReindex: startBlock !== undefined,
199676
+ verification
199613
199677
  });
199614
199678
  if (result.action === "unchanged") {
199615
199679
  info(`Subgraph "${effectiveDef.name}" is up to date (v${result.version} — no changes)`);
@@ -199622,6 +199686,9 @@ deploy does not backfill from genesis.`).action(async (file, options) => {
199622
199686
  } else {
199623
199687
  success(`Subgraph "${effectiveDef.name}" updated → v${result.version}`);
199624
199688
  }
199689
+ for (const line of formatVerificationLines(verification)) {
199690
+ info(` ${line}`);
199691
+ }
199625
199692
  await closeDb();
199626
199693
  }
199627
199694
  } catch (err) {
@@ -199675,6 +199742,8 @@ ${data.length} subgraph(s) total`));
199675
199742
  console.log(formatKeyValue([
199676
199743
  ["Name", subgraph.name],
199677
199744
  ["Version", subgraph.version],
199745
+ ["Pin", subgraph.pin ?? "none (deployed before pins)"],
199746
+ ["Verification", formatVerificationStatus(subgraph.verification)],
199678
199747
  [
199679
199748
  "Status",
199680
199749
  formatSubgraphStatus(subgraph.status, subgraph.health.lastError)
@@ -202526,7 +202595,7 @@ var {
202526
202595
  // package.json
202527
202596
  var package_default = {
202528
202597
  name: "@secondlayer/cli",
202529
- version: "19.2.1",
202598
+ version: "19.3.0",
202530
202599
  description: "Run a Stacks index on your own hardware — set it up, restore verified history, and keep it current.",
202531
202600
  repository: {
202532
202601
  type: "git",
@@ -202563,13 +202632,13 @@ var package_default = {
202563
202632
  "@biomejs/wasm-nodejs": "^1.9.0",
202564
202633
  "@dsnp/parquetjs": "~1.8.8",
202565
202634
  "@inquirer/prompts": "^8.2.0",
202566
- "@secondlayer/bundler": "^0.6.1",
202635
+ "@secondlayer/bundler": "^0.7.0",
202567
202636
  "@secondlayer/scaffold": "^1.6.1",
202568
202637
  "@secondlayer/sdk": "^14.3.0",
202569
- "@secondlayer/shared": "^11.20.0",
202638
+ "@secondlayer/shared": "^11.22.0",
202570
202639
  "@secondlayer/stacks": "^6.1.1",
202571
- "@secondlayer/subgraphs": "^6.3.6",
202572
- "@secondlayer/verify": "^0.1.0",
202640
+ "@secondlayer/subgraphs": "^6.4.0",
202641
+ "@secondlayer/verify": "^0.2.0",
202573
202642
  esbuild: "^0.28.2",
202574
202643
  "smol-toml": "^1.8.0",
202575
202644
  zod: "^4.3.6"
@@ -204015,6 +204084,7 @@ import {
204015
204084
  unhex
204016
204085
  } from "@secondlayer/verify";
204017
204086
  var LINKS = [
204087
+ { step: "ancestry", label: "ancestry" },
204018
204088
  { step: "block", label: "block" },
204019
204089
  { step: "bitcoin", label: "bitcoin" },
204020
204090
  { step: "burn", label: "burn" },
@@ -204065,7 +204135,9 @@ function prepareSource(src) {
204065
204135
  return src.getBitcoinHeaders(from, count);
204066
204136
  }
204067
204137
  };
204068
- const { getStateWrites, getVmEvents } = src;
204138
+ const { getEpoch2Header, getStateWrites, getVmEvents } = src;
204139
+ if (getEpoch2Header)
204140
+ out.getEpoch2Header = (id) => getEpoch2Header.call(src, id);
204069
204141
  if (getStateWrites)
204070
204142
  out.getStateWrites = (height) => getStateWrites.call(src, height);
204071
204143
  if (getVmEvents)
@@ -204077,6 +204149,14 @@ function prepareSource(src) {
204077
204149
  }
204078
204150
  function linkDetail(step, r, cp, rows) {
204079
204151
  switch (step) {
204152
+ case "ancestry": {
204153
+ const a = r.ancestry;
204154
+ if (!a)
204155
+ return "";
204156
+ const checkpoint = parseNakamotoHeader(unhex(cp.stacks.header));
204157
+ const start = a.fromHeight === Number(checkpoint.chainLength) ? `the checkpoint ${n(a.fromHeight)}` : `block ${n(a.fromHeight)}`;
204158
+ return a.via === "marf" ? `${n(r.height ?? 0)} proven in the state of ${start} (one MARF proof)` : `${n(r.height ?? 0)} reached from ${start} by parent links`;
204159
+ }
204080
204160
  case "block":
204081
204161
  return `${n(r.height ?? 0)} ${r.blockId}`;
204082
204162
  case "bitcoin":
@@ -204101,18 +204181,29 @@ function linkDetail(step, r, cp, rows) {
204101
204181
  return `${n(r.rowsChecked ?? 0)} vm_events rows match the diff`;
204102
204182
  }
204103
204183
  }
204184
+ function notNeeded(step, r) {
204185
+ if (!r.ancestry)
204186
+ return false;
204187
+ if (step === "bitcoin" || step === "burn")
204188
+ return r.burnHeight === undefined;
204189
+ return step === "signer-set" || step === "signatures";
204190
+ }
204104
204191
  function report(r, cp, rows) {
204105
204192
  const first = r.failures[0];
204106
204193
  const broken = first ? LINKS.findIndex((l) => l.step === first.step) : LINKS.length;
204107
204194
  for (const [i, link] of LINKS.entries()) {
204108
204195
  const label = link.label.padEnd(LABEL_WIDTH);
204109
204196
  const failure = r.failures.find((f) => f.step === link.step);
204197
+ if (link.step === "ancestry" && !failure && !r.ancestry)
204198
+ continue;
204110
204199
  if (failure)
204111
204200
  writeData(`${red("✗")} ${label} ${failure.message}`);
204112
204201
  else if (link.step === "rows" && !rows)
204113
204202
  writeData(dim(`· ${label} not checked (pass --rows)`));
204114
204203
  else if (i > broken)
204115
204204
  writeData(dim(`· ${label} not checked: an earlier link is broken`));
204205
+ else if (notNeeded(link.step, r))
204206
+ writeData(dim(`· ${label} not needed: the block is hash-chained to the checkpoint`));
204116
204207
  else if (link.step === "rows" && r.rowsChecked === undefined)
204117
204208
  writeData(dim(`· ${label} not checked: the source serves no indexed rows`));
204118
204209
  else
@@ -204161,6 +204252,8 @@ async function runVerifyBlock(ref, opts, deps = {}) {
204161
204252
  function attachVerifyBlockCommand(verify) {
204162
204253
  return verify.command("block").description("Prove one Stacks block from the checkpoint built into this release, trusting nothing else").argument("<ref>", "block height or index block hash").option("--node <url>", "read blocks and MARF proofs from this Stacks node RPC instead of the API").option("--checkpoint <file>", "trust this checkpoint JSON instead of the one built into this release").option("--rows", "also prove the indexer's rows for the block (state_writes, vm_events) against its state diff").addHelpText("after", `
204163
204254
  What is proven, link by link:
204255
+ ancestry below the checkpoint only: the block's id, from the checkpoint by
204256
+ parent links or one MARF proof (signer links are then not needed)
204164
204257
  bitcoin headers from the checkpoint to the block's burn block: proof-of-work and retargets
204165
204258
  burn the block's consensus hash commits to that Bitcoin block
204166
204259
  cycle the reward cycle's signer set, proven forward from the checkpoint's set
@@ -205277,7 +205370,7 @@ import {
205277
205370
  statSync as statSync2,
205278
205371
  writeFileSync as writeFileSync4
205279
205372
  } from "node:fs";
205280
- import { dirname as dirname6, join as join9, relative, resolve as resolve7 } from "node:path";
205373
+ import { dirname as dirname6, join as join9, relative as relative2, resolve as resolve7 } from "node:path";
205281
205374
  import { fileURLToPath as fileURLToPath4 } from "node:url";
205282
205375
  import { input, select as select3 } from "@inquirer/prompts";
205283
205376
  import { SecondLayer as SecondLayer4 } from "@secondlayer/sdk";
@@ -205459,7 +205552,7 @@ async function createWebhook(name, opts) {
205459
205552
  const targetDir = resolve7(process.cwd(), name);
205460
205553
  if (opts.scaffold !== false) {
205461
205554
  if (existsSync5(targetDir)) {
205462
- error(`Directory already exists: ${relative(process.cwd(), targetDir)}`);
205555
+ error(`Directory already exists: ${relative2(process.cwd(), targetDir)}`);
205463
205556
  process.exit(1);
205464
205557
  }
205465
205558
  const tplRoot = templatesRoot();
@@ -205506,7 +205599,7 @@ async function createWebhook(name, opts) {
205506
205599
  }
205507
205600
  if (signingSecret && opts.scaffold !== false) {
205508
205601
  const envTarget = writeSigningSecretEnv(targetDir, signingSecret);
205509
- success(`Signing secret written to ${relative(process.cwd(), envTarget)}`);
205602
+ success(`Signing secret written to ${relative2(process.cwd(), envTarget)}`);
205510
205603
  } else if (signingSecret && opts.scaffold === false) {
205511
205604
  info(`Signing secret (store securely): ${signingSecret}`);
205512
205605
  }
@@ -209505,5 +209598,5 @@ registerCreditsCommand(program);
209505
209598
  registerArchiveCommand(program);
209506
209599
  program.parse();
209507
209600
 
209508
- //# debugId=063D7FC6388E8ABC64756E2164756E21
209601
+ //# debugId=ECF90476FCFF9CB564756E2164756E21
209509
209602
  //# sourceMappingURL=cli.js.map