bulletin-deploy 0.14.1 → 0.14.2

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.
Files changed (47) hide show
  1. package/assets/environments.json +3 -0
  2. package/bin/bulletin-bootstrap +7 -0
  3. package/dist/auth-config.js +4 -4
  4. package/dist/bug-report.js +4 -4
  5. package/dist/chain-health.d.ts +139 -0
  6. package/dist/chain-health.js +14 -0
  7. package/dist/{chunk-DAFCH55Q.js → chunk-37AN2MHU.js} +2 -2
  8. package/dist/{chunk-42BNXFWF.js → chunk-6LD3Z2HM.js} +39 -36
  9. package/dist/{chunk-YDV7M3V2.js → chunk-7V4B775S.js} +3 -3
  10. package/dist/{chunk-ALTGYSBJ.js → chunk-CRZSDUWV.js} +1 -1
  11. package/dist/{chunk-AB62NERW.js → chunk-FMQLBYOR.js} +15 -8
  12. package/dist/{chunk-L7DCLCCK.js → chunk-GEHOB4PE.js} +1 -1
  13. package/dist/{chunk-4AEFFDHP.js → chunk-IJUKTHEP.js} +8 -2
  14. package/dist/{chunk-5ZR5L37D.js → chunk-REB37WWY.js} +4 -4
  15. package/dist/{chunk-PQK6XT6Z.js → chunk-RIQWW2BT.js} +8 -8
  16. package/dist/{chunk-5NQHQFMH.js → chunk-T2S5M6OH.js} +1 -1
  17. package/dist/chunk-UDJAYGYU.js +226 -0
  18. package/dist/{chunk-NLRGFZMQ.js → chunk-VH2HNQG2.js} +17 -1
  19. package/dist/{chunk-E6J46GNK.js → chunk-XQHRKK7M.js} +5 -1
  20. package/dist/{chunk-O5TJWVGZ.js → chunk-ZPWTXUA6.js} +7 -6
  21. package/dist/chunk-probe.js +3 -3
  22. package/dist/commands/login.js +13 -12
  23. package/dist/commands/logout.js +5 -5
  24. package/dist/commands/transfer.js +6 -5
  25. package/dist/commands/whoami.js +4 -4
  26. package/dist/deploy-actors.js +8 -7
  27. package/dist/deploy.js +13 -12
  28. package/dist/dotns.d.ts +2 -0
  29. package/dist/dotns.js +6 -5
  30. package/dist/environments.d.ts +2 -0
  31. package/dist/environments.js +1 -1
  32. package/dist/index.js +23 -22
  33. package/dist/manifest/publish.js +14 -13
  34. package/dist/memory-report.js +2 -2
  35. package/dist/merkle.js +13 -12
  36. package/dist/personhood/bootstrap.js +6 -5
  37. package/dist/personhood/people-client.js +6 -5
  38. package/dist/pool.d.ts +2 -0
  39. package/dist/pool.js +1 -1
  40. package/dist/run-state.js +1 -1
  41. package/dist/sss-allowance-cache.js +5 -5
  42. package/dist/storage-signer.js +13 -12
  43. package/dist/telemetry.d.ts +23 -1
  44. package/dist/telemetry.js +4 -2
  45. package/dist/version-check.js +3 -3
  46. package/package.json +7 -6
  47. package/tools/release-retry-wrapper.mjs +7 -0
@@ -6,6 +6,7 @@
6
6
  "network": "testnet",
7
7
  "description": "Product Preview net, used by Product Teams",
8
8
  "bulletinAutoAuthorize": true,
9
+ "bulletinAuthorizer": "//Eve",
9
10
  "e2eEligible": true,
10
11
  "backend": "https://polkadot-app-stg.parity.io/",
11
12
  "ipfs": "https://previewnet.substrate.dev",
@@ -43,6 +44,7 @@
43
44
  "description": "Previewnet bare-Rust PolkaVM DotNS deployment",
44
45
  "ipfs": "https://previewnet.substrate.dev",
45
46
  "webGateway": "testnet.li",
47
+ "bulletinAuthorizer": "//Eve",
46
48
  "autoAccountMapping": true,
47
49
  "nativeToEthRatio": 100000000,
48
50
  "registerStorageDeposit": 2000000000000,
@@ -99,6 +101,7 @@
99
101
  "network": "testnet",
100
102
  "description": "Next iteration of the Paseo Next testnet",
101
103
  "bulletinAutoAuthorize": true,
104
+ "bulletinAuthorizer": "//Alice",
102
105
  "e2eEligible": true,
103
106
  "backend": "https://identity-backend-next.parity-testnet.parity.io",
104
107
  "ipfs": "https://paseo-bulletin-next-ipfs.polkadot.io",
@@ -117,6 +117,13 @@ if (flags.env) {
117
117
 
118
118
  bootstrapOpts.bulletinAuthorizeV2 = envEntry.bulletinAuthorizeV2 ?? false;
119
119
 
120
+ // #1209: which key holds TransactionStorage authorizer rights on this env's
121
+ // Bulletin chain. Not always //Alice — previewnet's sole AllowedAuthorizers
122
+ // entry is //Eve. An explicit --authorizer still wins (set above).
123
+ if (envEntry.bulletinAuthorizer) {
124
+ bootstrapOpts.envAuthorizer = envEntry.bulletinAuthorizer;
125
+ }
126
+
120
127
  if (flags.preCheck) {
121
128
  // #1059: widen the "needs authorization" threshold to 24h-before-expiry,
122
129
  // and hard-gate the actual write to environments cleared for it. This is
@@ -9,12 +9,12 @@ import {
9
9
  getPeopleChainEndpoints,
10
10
  hasPersistedSession,
11
11
  resolveBulletinEndpoints
12
- } from "./chunk-5ZR5L37D.js";
12
+ } from "./chunk-REB37WWY.js";
13
13
  import "./chunk-TSPERKUS.js";
14
- import "./chunk-NLRGFZMQ.js";
15
- import "./chunk-O5TJWVGZ.js";
16
- import "./chunk-E6J46GNK.js";
14
+ import "./chunk-XQHRKK7M.js";
17
15
  import "./chunk-ZOC4GITL.js";
16
+ import "./chunk-VH2HNQG2.js";
17
+ import "./chunk-ZPWTXUA6.js";
18
18
  export {
19
19
  DOT_DAPP_ID,
20
20
  DOT_DERIVATION_INDEX,
@@ -10,10 +10,10 @@ import {
10
10
  offerBugReport,
11
11
  scrubSecrets,
12
12
  setDeployContext
13
- } from "./chunk-YDV7M3V2.js";
14
- import "./chunk-L7DCLCCK.js";
15
- import "./chunk-NLRGFZMQ.js";
16
- import "./chunk-O5TJWVGZ.js";
13
+ } from "./chunk-7V4B775S.js";
14
+ import "./chunk-GEHOB4PE.js";
15
+ import "./chunk-VH2HNQG2.js";
16
+ import "./chunk-ZPWTXUA6.js";
17
17
  export {
18
18
  buildCliFlagsSummary,
19
19
  buildLabels,
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Chain-health sampling — measures what the chain was doing underneath a deploy.
3
+ *
4
+ * Motivation: deploy latency drifted ~17% on paseo-next-v2 over the second half
5
+ * of July 2026 while the Bulletin storage phase stayed flat, pointing at Asset
6
+ * Hub extrinsic inclusion. That diagnosis had to be reconstructed by subtracting
7
+ * child-span durations after the fact, because we emit nothing about the chain
8
+ * itself. This module closes that gap.
9
+ *
10
+ * Design note — why interval spans rather than numeric attributes:
11
+ * `@sentry/node` user-defined attributes come back string-typed in EAP no matter
12
+ * how they're set, so `avg()`/`p95()` refuse to run on them (see the numeric
13
+ * attribute caveat in CLAUDE.md). `span.duration` is a built-in numeric column
14
+ * and does aggregate. All three signals here are genuinely elapsed wall-clock
15
+ * intervals, so encoding each as a span whose duration IS the measurement is
16
+ * honest — nothing synthetic — and it is the only shape that gives us
17
+ * `p50(span.duration)` charts.
18
+ */
19
+ /** Which chain a sampler is watching. Kept to the two that bound deploy time. */
20
+ type ChainHealthChainId = "bulletin" | "asset-hub";
21
+ /**
22
+ * Minimal slice of papi's PolkadotClient that the sampler needs. Declared
23
+ * structurally rather than importing PolkadotClient so tests can drive the
24
+ * sampler with a scripted block stream and no chain.
25
+ */
26
+ interface ChainHealthClient {
27
+ blocks$: {
28
+ subscribe(observer: {
29
+ next: (block: {
30
+ hash: string;
31
+ number: number;
32
+ }) => void;
33
+ error?: (e: unknown) => void;
34
+ }): {
35
+ unsubscribe(): void;
36
+ };
37
+ };
38
+ finalizedBlock$: {
39
+ subscribe(observer: {
40
+ next: (block: {
41
+ hash: string;
42
+ number: number;
43
+ }) => void;
44
+ error?: (e: unknown) => void;
45
+ }): {
46
+ unsubscribe(): void;
47
+ };
48
+ };
49
+ getBlockHeader(hash: string): Promise<unknown>;
50
+ }
51
+ /**
52
+ * Blocks per measurement window. 10 smooths per-block jitter without delaying
53
+ * the first sample past a median-length deploy.
54
+ */
55
+ declare const DEFAULT_WINDOW_BLOCKS = 10;
56
+ /**
57
+ * Hard cap on spans emitted per sampler, per deploy. A pathological multi-hour
58
+ * deploy must not emit thousands. Hitting it is reported, never silent.
59
+ */
60
+ declare const MAX_SPANS_PER_SAMPLER = 60;
61
+ /**
62
+ * Strip a wss:// endpoint down to its host. The endpoints in environments.json
63
+ * carry no credentials today; host-only keeps it that way by construction and
64
+ * keeps attribute cardinality at a handful of values.
65
+ */
66
+ declare function endpointHost(endpoint: string): string;
67
+ /**
68
+ * Watches one chain and emits `chain.health.*` interval spans.
69
+ *
70
+ * Lifecycle is attach/detach rather than a one-shot start/stop because the
71
+ * Bulletin client is destroyed and recreated mid-deploy during WS-halt
72
+ * recovery (deploy.ts) and the Asset Hub client is recreated by
73
+ * `recreateReviveClient` (dotns.ts, the #1131 retry path). Each recreation
74
+ * re-attaches; the in-flight window is discarded rather than emitted, because
75
+ * a window spanning a reconnect measures our outage, not the chain's block
76
+ * rate, and would silently poison the metric this exists to provide.
77
+ */
78
+ declare class ChainHealthSampler {
79
+ private readonly chainId;
80
+ private readonly windowBlocks;
81
+ private host;
82
+ private blocksSub;
83
+ private finalizedSub;
84
+ /** Wall-clock of the block that opened the current window. */
85
+ private windowStartMs;
86
+ private windowStartBlock;
87
+ /** blockNumber -> first-seen wall clock, FIFO-evicted at MAX_TRACKED_BLOCKS. */
88
+ private firstSeen;
89
+ private spansEmitted;
90
+ private capReported;
91
+ /** Set once a window closes, so exactly one ping is issued per window. */
92
+ private pingInFlight;
93
+ /**
94
+ * True while `blocks$.subscribe()` is replaying history.
95
+ *
96
+ * papi documents that on subscribe it synchronously emits the latest
97
+ * finalized block and every known descendant. Those arrive microseconds
98
+ * apart, so letting them close a window would report a near-zero elapsed
99
+ * time and mark the chain as impossibly fast — corrupting the exact metric
100
+ * this module exists to produce. Replayed blocks still record first-seen
101
+ * times (finality lag needs them) and still move the window anchor forward,
102
+ * so timing starts from the moment we caught up rather than in stale history.
103
+ */
104
+ private inReplay;
105
+ constructor(chainId: ChainHealthChainId, windowBlocks?: number);
106
+ /**
107
+ * Subscribe to a (re)created client. Safe to call repeatedly — an existing
108
+ * subscription is torn down first. Never throws: a sampler failure must not
109
+ * be able to fail a deploy.
110
+ */
111
+ attach(client: ChainHealthClient, endpoint: string): void;
112
+ /** Unsubscribe and drop the partial window. Idempotent. */
113
+ detach(): void;
114
+ /**
115
+ * Detach and reset per-deploy state. Call from the deploy's teardown.
116
+ *
117
+ * Resetting the span budget here (rather than in attach()) is deliberate.
118
+ * attach() also runs on mid-deploy reconnects, where clearing the counter
119
+ * would defeat the cap; stop() runs exactly once per deploy. deploy.ts holds
120
+ * its Bulletin sampler at module scope, so without this a second deploy() in
121
+ * the same process — playground-cli consumes deploy() as a library — would
122
+ * inherit the first deploy's exhausted budget and emit nothing.
123
+ */
124
+ stop(): void;
125
+ /** True while subscribed — used by tests and by re-attach guards. */
126
+ get attached(): boolean;
127
+ private resetWindow;
128
+ private atCap;
129
+ private onBlock;
130
+ private onFinalized;
131
+ /**
132
+ * Time one `getBlockHeader` round-trip. Separates "the chain is producing
133
+ * blocks slowly" from "our link to this endpoint is slow" — the endpoints are
134
+ * load-balanced, so those are genuinely different failures.
135
+ */
136
+ private ping;
137
+ }
138
+
139
+ export { type ChainHealthChainId, type ChainHealthClient, ChainHealthSampler, DEFAULT_WINDOW_BLOCKS, MAX_SPANS_PER_SAMPLER, endpointHost };
@@ -0,0 +1,14 @@
1
+ import {
2
+ ChainHealthSampler,
3
+ DEFAULT_WINDOW_BLOCKS,
4
+ MAX_SPANS_PER_SAMPLER,
5
+ endpointHost
6
+ } from "./chunk-UDJAYGYU.js";
7
+ import "./chunk-VH2HNQG2.js";
8
+ import "./chunk-ZPWTXUA6.js";
9
+ export {
10
+ ChainHealthSampler,
11
+ DEFAULT_WINDOW_BLOCKS,
12
+ MAX_SPANS_PER_SAMPLER,
13
+ endpointHost
14
+ };
@@ -4,10 +4,10 @@ import {
4
4
  } from "./chunk-5FLTDWWP.js";
5
5
  import {
6
6
  DOT_PRODUCT_ID
7
- } from "./chunk-5ZR5L37D.js";
7
+ } from "./chunk-REB37WWY.js";
8
8
  import {
9
9
  DEFAULT_MNEMONIC
10
- } from "./chunk-AB62NERW.js";
10
+ } from "./chunk-FMQLBYOR.js";
11
11
 
12
12
  // src/deploy-actors.ts
13
13
  var DEFAULT_WORKER_SURI = DEFAULT_MNEMONIC;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  preflightSssAllowance
3
- } from "./chunk-5NQHQFMH.js";
3
+ } from "./chunk-T2S5M6OH.js";
4
4
  import {
5
5
  statementSigningAccount
6
6
  } from "./chunk-GRPLHUYC.js";
@@ -31,18 +31,18 @@ import {
31
31
  } from "./chunk-S7EM5VMW.js";
32
32
  import {
33
33
  setDeployContext
34
- } from "./chunk-YDV7M3V2.js";
34
+ } from "./chunk-7V4B775S.js";
35
35
  import {
36
36
  getBestBlockNumber,
37
37
  probeChunks,
38
38
  probeFinalityGap
39
- } from "./chunk-ALTGYSBJ.js";
39
+ } from "./chunk-CRZSDUWV.js";
40
40
  import {
41
41
  packSection
42
42
  } from "./chunk-C2TS5MER.js";
43
43
  import {
44
44
  resolveStorageSigner
45
- } from "./chunk-DAFCH55Q.js";
45
+ } from "./chunk-37AN2MHU.js";
46
46
  import {
47
47
  createSlotAccountSigner,
48
48
  requestResourceAllocation
@@ -51,7 +51,7 @@ import {
51
51
  STALE_SESSION_MESSAGE,
52
52
  getPeopleChainEndpoints,
53
53
  hasPersistedSession
54
- } from "./chunk-5ZR5L37D.js";
54
+ } from "./chunk-REB37WWY.js";
55
55
  import {
56
56
  CLI_NAME
57
57
  } from "./chunk-TSPERKUS.js";
@@ -66,7 +66,7 @@ import {
66
66
  parseDomainName,
67
67
  popStatusName,
68
68
  verifyNonceAdvanced
69
- } from "./chunk-AB62NERW.js";
69
+ } from "./chunk-FMQLBYOR.js";
70
70
  import {
71
71
  derivePoolAccounts,
72
72
  detectTestnet,
@@ -74,7 +74,19 @@ import {
74
74
  fetchPoolAuthorizations,
75
75
  isAuthorizationSufficient,
76
76
  selectAccount
77
- } from "./chunk-4AEFFDHP.js";
77
+ } from "./chunk-IJUKTHEP.js";
78
+ import {
79
+ DEFAULT_ENV_ID,
80
+ getPopSelfServeConfig,
81
+ loadEnvironments,
82
+ resolveEndpoints
83
+ } from "./chunk-XQHRKK7M.js";
84
+ import {
85
+ NonRetryableError
86
+ } from "./chunk-ZOC4GITL.js";
87
+ import {
88
+ ChainHealthSampler
89
+ } from "./chunk-UDJAYGYU.js";
78
90
  import {
79
91
  VERSION,
80
92
  captureWarning,
@@ -88,16 +100,7 @@ import {
88
100
  truncateAddress,
89
101
  withDeploySpan,
90
102
  withSpan
91
- } from "./chunk-NLRGFZMQ.js";
92
- import {
93
- DEFAULT_ENV_ID,
94
- getPopSelfServeConfig,
95
- loadEnvironments,
96
- resolveEndpoints
97
- } from "./chunk-E6J46GNK.js";
98
- import {
99
- NonRetryableError
100
- } from "./chunk-ZOC4GITL.js";
103
+ } from "./chunk-VH2HNQG2.js";
101
104
 
102
105
  // src/merkle.ts
103
106
  import * as fs2 from "fs";
@@ -416,6 +419,7 @@ function setBulletinEndpoints(endpoints) {
416
419
  BULLETIN_ENDPOINTS = endpoints;
417
420
  }
418
421
  var bulletinAutoAuthorize = false;
422
+ var bulletinAuthorizer;
419
423
  var _deployRpcFailedOver = false;
420
424
  var _onWsHalt = null;
421
425
  function setWsHaltCallback(cb) {
@@ -542,14 +546,22 @@ function toHashingEnum(mhCode) {
542
546
  throw new Error(`Unhandled multihash code: ${mhCode}`);
543
547
  }
544
548
  }
545
- async function getProvider() {
549
+ var bulletinChainHealth = new ChainHealthSampler("bulletin");
550
+ function stopBulletinChainHealth() {
551
+ bulletinChainHealth.stop();
552
+ }
553
+ function createBulletinClient() {
546
554
  const primary = BULLETIN_ENDPOINTS[0];
547
555
  console.log(` Connecting to Bulletin: ${primary}`);
548
556
  const client = createPolkadotClient2(getWsProvider2(
549
557
  BULLETIN_ENDPOINTS,
550
558
  { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
551
559
  ));
552
- const unsafeApi = client.getUnsafeApi();
560
+ bulletinChainHealth.attach(client, primary);
561
+ return { client, unsafeApi: client.getUnsafeApi() };
562
+ }
563
+ async function getProvider() {
564
+ const { client, unsafeApi } = createBulletinClient();
553
565
  try {
554
566
  await cryptoWaitReady();
555
567
  const poolMnemonic = process.env.BULLETIN_POOL_MNEMONIC || void 0;
@@ -567,7 +579,7 @@ async function getProvider() {
567
579
  const selectionResult = selectAccount(authorizations, Math.random, pinnedPoolIndex);
568
580
  const selectedAccount = selectionResult.account;
569
581
  const eligibleCount = selectionResult.eligibleCount;
570
- await ensureAuthorized(unsafeApi, selectedAccount.address, `pool account ${selectedAccount.index}`, { autoAuthorize: bulletinAutoAuthorize });
582
+ await ensureAuthorized(unsafeApi, selectedAccount.address, `pool account ${selectedAccount.index}`, { autoAuthorize: bulletinAutoAuthorize, authorizer: bulletinAuthorizer });
571
583
  console.log(` Using pool account ${selectedAccount.index}: ${selectedAccount.address}`);
572
584
  setDeployAttribute("deploy.signer.mode", "pool");
573
585
  setDeployAttribute("deploy.pool.account", truncateAddress(selectedAccount.address));
@@ -580,13 +592,7 @@ async function getProvider() {
580
592
  }
581
593
  }
582
594
  async function getDirectProvider(mnemonic, derivationPath = "") {
583
- const primary = BULLETIN_ENDPOINTS[0];
584
- console.log(` Connecting to Bulletin: ${primary}`);
585
- const client = createPolkadotClient2(getWsProvider2(
586
- BULLETIN_ENDPOINTS,
587
- { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
588
- ));
589
- const unsafeApi = client.getUnsafeApi();
595
+ const { client, unsafeApi } = createBulletinClient();
590
596
  const { signer, ss58 } = deriveRootSigner(mnemonic, derivationPath);
591
597
  console.log(` Using direct signer: ${ss58}${derivationPath ? ` (path: ${derivationPath})` : ""}`);
592
598
  let [auth, currentBlock] = await Promise.all([
@@ -596,7 +602,7 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
596
602
  let now = currentBlock.number;
597
603
  if (!auth || Number(auth.expiration ?? 0) <= now) {
598
604
  try {
599
- await ensureAuthorized(unsafeApi, ss58, "direct signer", { autoAuthorize: bulletinAutoAuthorize });
605
+ await ensureAuthorized(unsafeApi, ss58, "direct signer", { autoAuthorize: bulletinAutoAuthorize, authorizer: bulletinAuthorizer });
600
606
  [auth, currentBlock] = await Promise.all([
601
607
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
602
608
  client.getFinalizedBlock()
@@ -613,13 +619,7 @@ async function getDirectProvider(mnemonic, derivationPath = "") {
613
619
  return { client, unsafeApi, signer, ss58 };
614
620
  }
615
621
  async function getSignerProvider(signer, ss58) {
616
- const primary = BULLETIN_ENDPOINTS[0];
617
- console.log(` Connecting to Bulletin: ${primary}`);
618
- const client = createPolkadotClient2(getWsProvider2(
619
- BULLETIN_ENDPOINTS,
620
- { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS, onStatusChanged: makeBulletinStatusHandler(primary) }
621
- ));
622
- const unsafeApi = client.getUnsafeApi();
622
+ const { client, unsafeApi } = createBulletinClient();
623
623
  console.log(` Using external signer: ${ss58}`);
624
624
  let [auth, currentBlock] = await Promise.all([
625
625
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
@@ -628,7 +628,7 @@ async function getSignerProvider(signer, ss58) {
628
628
  let now = currentBlock.number;
629
629
  if (!auth || Number(auth.expiration ?? 0) <= now) {
630
630
  try {
631
- await ensureAuthorized(unsafeApi, ss58, "external signer", { autoAuthorize: bulletinAutoAuthorize });
631
+ await ensureAuthorized(unsafeApi, ss58, "external signer", { autoAuthorize: bulletinAutoAuthorize, authorizer: bulletinAuthorizer });
632
632
  [auth, currentBlock] = await Promise.all([
633
633
  unsafeApi.query.TransactionStorage.Authorizations.getValue(Enum2("Account", ss58)),
634
634
  client.getFinalizedBlock()
@@ -2130,6 +2130,7 @@ async function deploy(content, domainName = null, options = {}) {
2130
2130
  let envRegisterStorageDeposit;
2131
2131
  let envPopSelfServe = null;
2132
2132
  bulletinAutoAuthorize = false;
2133
+ bulletinAuthorizer = void 0;
2133
2134
  if (options.bulletinEndpoints && options.bulletinEndpoints.length > 0) {
2134
2135
  envBulletin = options.bulletinEndpoints;
2135
2136
  envAssetHub = options.assetHubEndpoints;
@@ -2150,6 +2151,7 @@ async function deploy(content, domainName = null, options = {}) {
2150
2151
  envRegisterStorageDeposit = resolved.registerStorageDeposit;
2151
2152
  envPopSelfServe = getPopSelfServeConfig(doc, envId);
2152
2153
  bulletinAutoAuthorize = resolved.bulletinAutoAuthorize;
2154
+ bulletinAuthorizer = resolved.bulletinAuthorizer;
2153
2155
  } catch (e) {
2154
2156
  if (e instanceof NonRetryableError) throw e;
2155
2157
  if (options.env !== void 0) throw e;
@@ -2718,6 +2720,7 @@ async function deploy(content, domainName = null, options = {}) {
2718
2720
  return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
2719
2721
  } finally {
2720
2722
  if (_deployRpcFailedOver) setDeployAttribute("deploy.rpc.failed_over", "true");
2723
+ stopBulletinChainHealth();
2721
2724
  provider?.client.destroy();
2722
2725
  }
2723
2726
  });
@@ -2,15 +2,15 @@ import {
2
2
  classifyErrorArea,
3
3
  isInteractive,
4
4
  promptYesNo
5
- } from "./chunk-L7DCLCCK.js";
5
+ } from "./chunk-GEHOB4PE.js";
6
6
  import {
7
7
  VERSION,
8
8
  getCurrentSentryTraceId,
9
9
  resolveIssueRepoSlug
10
- } from "./chunk-NLRGFZMQ.js";
10
+ } from "./chunk-VH2HNQG2.js";
11
11
  import {
12
12
  package_default
13
- } from "./chunk-O5TJWVGZ.js";
13
+ } from "./chunk-ZPWTXUA6.js";
14
14
 
15
15
  // src/bug-report.ts
16
16
  import { execSync, execFileSync } from "child_process";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  captureWarning
3
- } from "./chunk-NLRGFZMQ.js";
3
+ } from "./chunk-VH2HNQG2.js";
4
4
 
5
5
  // src/chunk-probe.ts
6
6
  import { Twox128, Blake2128Concat, decAnyMetadata, unifyMetadata } from "@polkadot-api/substrate-bindings";
@@ -3,7 +3,16 @@ import {
3
3
  } from "./chunk-SI2ZUOYD.js";
4
4
  import {
5
5
  isTestnetSpecName
6
- } from "./chunk-4AEFFDHP.js";
6
+ } from "./chunk-IJUKTHEP.js";
7
+ import {
8
+ validateContractAddresses
9
+ } from "./chunk-XQHRKK7M.js";
10
+ import {
11
+ NonRetryableError
12
+ } from "./chunk-ZOC4GITL.js";
13
+ import {
14
+ ChainHealthSampler
15
+ } from "./chunk-UDJAYGYU.js";
7
16
  import {
8
17
  captureWarning,
9
18
  markCodePath,
@@ -11,13 +20,7 @@ import {
11
20
  setDeploySentryTag,
12
21
  truncateAddress,
13
22
  withSpan
14
- } from "./chunk-NLRGFZMQ.js";
15
- import {
16
- validateContractAddresses
17
- } from "./chunk-E6J46GNK.js";
18
- import {
19
- NonRetryableError
20
- } from "./chunk-ZOC4GITL.js";
23
+ } from "./chunk-VH2HNQG2.js";
21
24
 
22
25
  // src/dotns.ts
23
26
  import crypto from "crypto";
@@ -1238,6 +1241,8 @@ var DotNS = class {
1238
1241
  _phoneSignatureTotal = 0;
1239
1242
  /** Running attempt counter per label for re-sign detection. Reset at connect/disconnect. */
1240
1243
  _phoneSignatureAttempts = /* @__PURE__ */ new Map();
1244
+ /** Samples Asset Hub block rate / finality lag / RPC latency for the life of this connection. */
1245
+ chainHealth = new ChainHealthSampler("asset-hub");
1241
1246
  // Test-only seam: consumed once by classifyAliasAccountState() then cleared.
1242
1247
  // Mirrors the __setDeployRootSpanForTest / __setSentryForTest pattern.
1243
1248
  _classifyOverrideForTest = null;
@@ -1285,6 +1290,7 @@ var DotNS = class {
1285
1290
  }
1286
1291
  this.client = createClient(getWsProvider(endpoint, { heartbeatTimeout: WS_HEARTBEAT_TIMEOUT_MS }));
1287
1292
  this.clientWrapper = new ReviveClientWrapper(this.client.getUnsafeApi());
1293
+ this.chainHealth.attach(this.client, endpoint);
1288
1294
  }
1289
1295
  async connect(options = {}) {
1290
1296
  if (options.assetHubEndpoints && options.assetHubEndpoints.length > 0) {
@@ -3067,6 +3073,7 @@ var DotNS = class {
3067
3073
  this._onPhoneSigningRequired?.(label);
3068
3074
  }
3069
3075
  disconnect() {
3076
+ this.chainHealth.stop();
3070
3077
  if (this.client) {
3071
3078
  this.client.destroy();
3072
3079
  this.client = null;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-NLRGFZMQ.js";
3
+ } from "./chunk-VH2HNQG2.js";
4
4
 
5
5
  // src/version-check.ts
6
6
  import { execSync, execFileSync } from "child_process";
@@ -127,7 +127,8 @@ async function ensureAuthorized(api, address, label, opts = {}) {
127
127
  if (opts.autoAuthorize) {
128
128
  await cryptoWaitReady();
129
129
  const keyring = new Keyring({ type: "sr25519" });
130
- const alice = keyring.addFromUri("//Alice");
130
+ const authorizerUri = opts.authorizer ?? "//Alice";
131
+ const alice = keyring.addFromUri(authorizerUri);
131
132
  const signer = getPolkadotSigner(alice.publicKey, "Sr25519", (data) => alice.sign(data));
132
133
  const grant = `${TOPUP_TRANSACTIONS} txs / ${Number(TOPUP_BYTES) / 1e6}MB`;
133
134
  console.log(` Auto-authorizing ${who} on Bulletin (${grant})...`);
@@ -139,7 +140,7 @@ async function ensureAuthorized(api, address, label, opts = {}) {
139
140
  const result = await tx.signAndSubmit(signer);
140
141
  if (!result?.ok) {
141
142
  throw new Error(
142
- `Auto-authorization of Bulletin storage account ${who} failed: authorize_account was rejected. This environment is flagged bulletinAutoAuthorize, but //Alice may not hold authorizer rights on this chain.`
143
+ `Auto-authorization of Bulletin storage account ${who} failed: authorize_account was rejected. This environment is flagged bulletinAutoAuthorize and signed as ${authorizerUri}, but that key may not hold authorizer rights on this chain \u2014 check TransactionStorage.AllowedAuthorizers and set the env's bulletinAuthorizer accordingly.`
143
144
  );
144
145
  }
145
146
  console.log(` Authorized: ${grant}`);
@@ -254,6 +255,11 @@ ${details}`
254
255
  const authKey = keyring.addFromUri(opts.authorizerMnemonic);
255
256
  authorizerSigner = getPolkadotSigner(authKey.publicKey, "Sr25519", (data) => authKey.sign(data));
256
257
  console.log(`Using provided authorizer: ${authKey.address}
258
+ `);
259
+ } else if (opts.envAuthorizer) {
260
+ const authKey = keyring.addFromUri(opts.envAuthorizer);
261
+ authorizerSigner = getPolkadotSigner(authKey.publicKey, "Sr25519", (data) => authKey.sign(data));
262
+ console.log(`Using environment authorizer ${opts.envAuthorizer} (${authKey.address})
257
263
  `);
258
264
  } else {
259
265
  const isTestnet = await detectTestnet(api);
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  CLI_NAME
3
3
  } from "./chunk-TSPERKUS.js";
4
- import {
5
- VERSION
6
- } from "./chunk-NLRGFZMQ.js";
7
4
  import {
8
5
  loadEnvironments
9
- } from "./chunk-E6J46GNK.js";
6
+ } from "./chunk-XQHRKK7M.js";
7
+ import {
8
+ VERSION
9
+ } from "./chunk-VH2HNQG2.js";
10
10
 
11
11
  // src/auth-config.ts
12
12
  import { existsSync, readdirSync } from "fs";
@@ -9,25 +9,25 @@ import {
9
9
  setBulletinEndpoints,
10
10
  storeDirectory,
11
11
  storeFile
12
- } from "./chunk-42BNXFWF.js";
12
+ } from "./chunk-6LD3Z2HM.js";
13
13
  import {
14
14
  DotNS,
15
15
  TX_KIND_SKIPPED
16
- } from "./chunk-AB62NERW.js";
17
- import {
18
- setDeployAttribute,
19
- withManifestSpan,
20
- withSpan
21
- } from "./chunk-NLRGFZMQ.js";
16
+ } from "./chunk-FMQLBYOR.js";
22
17
  import {
23
18
  DEFAULT_ENV_ID,
24
19
  getPopSelfServeConfig,
25
20
  loadEnvironments,
26
21
  resolveEndpoints
27
- } from "./chunk-E6J46GNK.js";
22
+ } from "./chunk-XQHRKK7M.js";
28
23
  import {
29
24
  NonRetryableError
30
25
  } from "./chunk-ZOC4GITL.js";
26
+ import {
27
+ setDeployAttribute,
28
+ withManifestSpan,
29
+ withSpan
30
+ } from "./chunk-VH2HNQG2.js";
31
31
 
32
32
  // src/manifest/publish.ts
33
33
  import * as fs from "fs/promises";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-GRPLHUYC.js";
4
4
  import {
5
5
  DOT_DAPP_ID
6
- } from "./chunk-5ZR5L37D.js";
6
+ } from "./chunk-REB37WWY.js";
7
7
 
8
8
  // src/sss-allowance-cache.ts
9
9
  import { mkdir, readFile, writeFile, unlink } from "fs/promises";