@parity/product-deploy 0.10.0-rc.3 → 0.10.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.
Files changed (47) hide show
  1. package/DEPLOYMENT.md +61 -83
  2. package/bin/bulletin-bootstrap +24 -9
  3. package/dist/auth/index.js +2 -1
  4. package/dist/auth/vendor/index.js +2 -1
  5. package/dist/auth-config.js +4 -3
  6. package/dist/bug-report.js +4 -4
  7. package/dist/{chunk-GKG2EQKM.js → chunk-32THWODT.js} +50 -21
  8. package/dist/{chunk-4PVJ2JBZ.js → chunk-4IUTMHVB.js} +73 -41
  9. package/dist/{chunk-5OKB3TEB.js → chunk-5FLTDWWP.js} +4 -1
  10. package/dist/{chunk-UBLIZRYW.js → chunk-7HQL3TPT.js} +1 -1
  11. package/dist/{chunk-FAB6BWDB.js → chunk-ARPBPQZQ.js} +2 -2
  12. package/dist/{chunk-7OOKA4BJ.js → chunk-F2SDBU57.js} +5 -2
  13. package/dist/{chunk-D2WW2CIA.js → chunk-OR7LUTET.js} +2 -2
  14. package/dist/{chunk-DFRIBV6O.js → chunk-ORIB2S5P.js} +1 -1
  15. package/dist/{chunk-EJ5QLQU4.js → chunk-QR6HCDY2.js} +5 -4
  16. package/dist/{chunk-IF6BNIKT.js → chunk-SNDIOANW.js} +1 -1
  17. package/dist/chunk-TSPERKUS.js +6 -0
  18. package/dist/{chunk-KY3CRS66.js → chunk-V4RTUMBT.js} +1 -1
  19. package/dist/{chunk-RI3ZLNPN.js → chunk-WIMRJK32.js} +1 -1
  20. package/dist/{chunk-ZG56UL3V.js → chunk-ZC43ORKF.js} +1 -1
  21. package/dist/chunk-probe.js +3 -3
  22. package/dist/commands/login.js +27 -18
  23. package/dist/commands/logout.js +6 -5
  24. package/dist/commands/transfer.js +8 -5
  25. package/dist/commands/whoami.js +7 -4
  26. package/dist/deploy-actors.js +6 -5
  27. package/dist/deploy.d.ts +19 -2
  28. package/dist/deploy.js +17 -10
  29. package/dist/dotns.js +4 -4
  30. package/dist/index.js +13 -12
  31. package/dist/manifest/byte-budget.d.ts +4 -5
  32. package/dist/manifest/byte-budget.js +1 -1
  33. package/dist/manifest/publish.js +13 -12
  34. package/dist/memory-report.js +2 -2
  35. package/dist/merkle.js +11 -10
  36. package/dist/personhood/bootstrap.js +4 -4
  37. package/dist/personhood/people-client.js +4 -4
  38. package/dist/pool.d.ts +7 -2
  39. package/dist/pool.js +3 -1
  40. package/dist/run-state.js +1 -1
  41. package/dist/sss-allowance-cache.js +5 -4
  42. package/dist/storage-signer.js +11 -10
  43. package/dist/telemetry.js +2 -2
  44. package/dist/version-check.js +3 -3
  45. package/docs/bootstrap.md +47 -13
  46. package/docs/e2e-bootstrap.md +5 -0
  47. package/package.json +1 -1
package/DEPLOYMENT.md CHANGED
@@ -1,124 +1,102 @@
1
- # Deploying your first app
1
+ # Setting up an environment
2
2
 
3
- This guide walks you from zero to a deployed, viewable web app on the [Polkadot Bulletin Chain](https://github.com/paritytech/polkadot-bulletin-chain) testnet, using `bulletin-deploy`.
3
+ This guide is for the **operator / chain admin** standing up `bulletin-deploy` against a Polkadot environment. It covers what you configure and authorize so that deploys work against *your* chain.
4
4
 
5
- > [!NOTE]
6
- > This targets a **testnet** (`paseo-next-v2`) by default. It is reference/proof-of-concept tooling — see the security notice in the [README](README.md#security).
7
-
8
- ## Prerequisites
5
+ For **using** the tool once an environment is set up — building and shipping an app — see the [README](README.md). This document is the setup side.
9
6
 
10
- 1. **Node.js ≥ 22.**
11
- ```sh
12
- node --version # must print v22 or higher
13
- ```
14
- 2. **The CLI**, installed globally:
15
- ```sh
16
- npm install -g bulletin-deploy
17
- ```
18
- 3. **A built static site** — a directory of static files (HTML/CSS/JS), for example `./dist`. Build your app however you normally would (`npm run build`, etc.); `bulletin-deploy` uploads the resulting directory as-is.
19
- 4. **An identity to own the name** — choose one:
20
- - **Mobile Polkadot wallet (recommended).** No mnemonic on disk; you sign in once by scanning a QR code. See [Sign in](#1-sign-in-recommended) below.
21
- - **A testnet mnemonic.** Pass `--mnemonic "..."` (or set the `MNEMONIC` env var) instead of signing in.
7
+ > [!NOTE]
8
+ > `bulletin-deploy` is reference / proof-of-concept tooling — see the security notice in the [README](README.md#security). The defaults target Polkadot testnets.
22
9
 
23
- That is all you need on testnet. You do **not** need testnet funds for the recommended path: a worker account registers the name and uploads the content, then transfers ownership to your account as the final step.
10
+ **Documentation map read in order:**
11
+ 1. **[DEPLOYMENT.md](DEPLOYMENT.md)** (this doc) — set up `bulletin-deploy` for your environment.
12
+ 2. **[docs/bootstrap.md](docs/bootstrap.md)** — the `bulletin-bootstrap` reference (Bulletin storage authorization).
13
+ 3. **[docs/e2e-bootstrap.md](docs/e2e-bootstrap.md)** — a fully worked setup, end to end, for the E2E test environment.
24
14
 
25
- ### Optional: IPFS / Kubo
15
+ (Using the tool to ship an app — not setting it up — is the [README](README.md).)
26
16
 
27
- By default the CLI uses the [IPFS Kubo](https://docs.ipfs.tech/install/) binary to merkleize your site if it is on your `PATH`, and otherwise falls back to a pure-JavaScript implementation. You can force the pure-JS path (no binary required) with `--js-merkle`. If you see `IPFS CLI not installed`, either install Kubo or add `--js-merkle`.
17
+ ## What an "environment" is
28
18
 
29
- ## Choosing a name
19
+ A `bulletin-deploy` environment is three things, which it expects to already exist for your network:
30
20
 
31
- Apps are addressed by a DotNS name like `myapp.dot`. A few rules:
21
+ - an **Asset Hub** (PolkaVM / `pallet-revive`) with the **DotNS** contracts deployed names live here;
22
+ - a **Bulletin Chain** — content (your site, chunked + content-addressed) is stored here;
23
+ - an **IPFS gateway** — serves the stored content over HTTP.
32
24
 
33
- - The base label must be **at least 6 characters** (shorter labels are reserved).
34
- - Pick something not already registered. The CLI tells you during a deploy whether the name is available or already owned by you.
25
+ Setup is: tell `bulletin-deploy` where those are, authorize the accounts that will write to the Bulletin Chain, and (if your DotNS gates registration) grant the registering account personhood. The steps below.
35
26
 
36
- ## Deploy
27
+ ## 1. Define the environment
37
28
 
38
- ### 1. Sign in (recommended)
29
+ `bulletin-deploy` resolves environments from `environments.json`. The built-in presets ship in `assets/environments.json`:
39
30
 
40
31
  ```sh
41
- bulletin-deploy login # scan the QR code with your Polkadot wallet app
42
- bulletin-deploy whoami # confirm the signed-in address
32
+ bulletin-deploy --list-environments # list the built-in environment IDs
43
33
  ```
44
34
 
45
- If you would rather sign with a mnemonic, skip this and pass `--mnemonic` on the deploy command instead.
46
-
47
- ### 2. Deploy your build directory
35
+ Add your network either by adding an entry to `assets/environments.json`, or by supplying one at runtime:
48
36
 
49
37
  ```sh
50
- bulletin-deploy ./dist myapp.dot
51
- # or, without an IPFS binary installed:
52
- bulletin-deploy ./dist myapp.dot --js-merkle
38
+ bulletin-deploy ./dist myapp.dot --environment-file ./my-env.json
39
+ bulletin-deploy ./dist myapp.dot --env <id> --contract DOTNS_REGISTRAR=0x... # override single fields
53
40
  ```
54
41
 
55
- The CLI will:
42
+ An environment entry provides:
56
43
 
57
- 1. Merkleize your directory into content-addressed chunks.
58
- 2. Upload the chunks to the Bulletin Chain and wait for finality.
59
- 3. Register `myapp.dot` in DotNS and point it at your content.
60
- 4. Transfer ownership of the name to your signed-in account.
44
+ - **Chain endpoints** the `wss://` RPCs for at least the **bulletin** and **asset-hub** chains.
45
+ - **DotNS contract addresses** the contract set deployed on your Asset Hub (`DOTNS_REGISTRAR`, `POP_RULES`, the resolvers, `STORE_FACTORY`, …).
46
+ - **`nativeToEthRatio`** `10^(18 native_decimals)`; e.g. a 10-decimal native token → `100000000`.
47
+ - **`registerStorageDeposit`** the DotNS registration deposit on your chain.
48
+ - **`autoAccountMapping`** — whether the chain maps accounts to H160 automatically on first tx.
49
+ - **`ipfs`** — the gateway that serves deployed content.
61
50
 
62
- On success it prints a summary:
51
+ Use the `paseo-next-v2` and `summit` entries in `assets/environments.json` as worked examples to copy.
63
52
 
64
- ```
65
- ============================================================
66
- DEPLOYMENT COMPLETE!
67
- ============================================================
68
- - Polkadot Desktop: myapp.dot
69
- - Polkadot Browser: https://myapp.dot.li
70
- ```
53
+ ## 2. Authorize Bulletin storage
71
54
 
72
- ### 3. View it
55
+ Writing content to the Bulletin Chain requires each **storage account** to hold a `TransactionStorage` **authorization** — a quota of transactions and bytes. Storage is gated by this quota, **not by balance** (Bulletin has no fee model), so the storage accounts need no funds. `bulletin-deploy` never grants the authorization itself; the chain's **authorizer** must.
73
56
 
74
- - Open **`https://myapp.dot.li`** in any browser.
75
- - Or open **`myapp.dot`** directly in a Polkadot-aware browser/extension.
57
+ The storage accounts are an upload **pool**, derived as `//deploy/0…N` from `BULLETIN_POOL_MNEMONIC` (default: the well-known `DEV_PHRASE`). The deploy path derives the **same** pool from that env var — so if you use a custom pool, set `BULLETIN_POOL_MNEMONIC` identically for both bootstrap and deploy, or they won't line up.
76
58
 
77
- ## If the transfer step fails
78
-
79
- If your content uploads but the final ownership transfer fails, the name is registered by the worker and you can claim it separately:
59
+ `bulletin-bootstrap` reports each pool account's authorization status, and — given an authorizer key — grants authorization to the ones that lack it:
80
60
 
81
61
  ```sh
82
- bulletin-deploy transfer myapp.dot # your signed-in account
83
- bulletin-deploy transfer myapp.dot --to 0x... # an explicit recipient
62
+ bulletin-bootstrap --env <id> # list pool accounts + their authorization status
63
+ bulletin-bootstrap --env <id> --authorizer "<seed>" # grant authorization with the authorizer key
64
+ bulletin-bootstrap --env <id> --pool-size 20 # inspect/authorize a larger pool
84
65
  ```
85
66
 
86
- ## Choosing a different network
67
+ On a **testnet** the authorizer defaults to `//Alice` (it holds authorization authority there), so no `--authorizer` is needed. On a **production** chain, pass `--authorizer` with the key that actually holds authorization authority — if it can't grant, the tool reports that rather than pretending. See [`docs/bootstrap.md`](docs/bootstrap.md) for the full reference.
87
68
 
88
- ```sh
89
- bulletin-deploy --list-environments # show available environment IDs
90
- bulletin-deploy ./dist myapp.dot --env <id>
91
- ```
69
+ ## 3. Personhood (if your DotNS gates registration)
92
70
 
93
- The default is `paseo-next-v2`. Override individual fields with `--environment-file <path>` or `--contract KEY=0x...`.
71
+ If your `POP_RULES` contract requires Proof-of-Personhood to register a base name, the **registering account** needs a PoP status granted by the `POP_RULES` owner. (NoStatus-eligible label shapes skip this — see the rules in your DotNS deployment.)
94
72
 
95
- ## Bootstrapping your own storage pool
73
+ - On Parity's testnets, request a grant by opening an issue on [paritytech/dotns](https://github.com/paritytech/dotns/issues).
74
+ - On your own chain, your `POP_RULES` owner grants it out of band — `bulletin-deploy` cannot upgrade a signer.
96
75
 
97
- On `paseo-next-v2` and the other shared Parity testnets the upload pool is **already authorized** — you don't need to bootstrap anything to deploy. You only need this if you run your own setup: a custom pool mnemonic, your own Bulletin chain, or a testnet that was just reset (a wipe clears all authorizations).
76
+ ## 4. Fund the signing accounts
98
77
 
99
- Uploading content to the Bulletin Chain requires the storage account to hold a `TransactionStorage` authorization a quota of transactions and bytes. `bulletin-deploy` never grants this itself; the account must be authorized by the chain's authorizer first. The separate `bulletin-bootstrap` CLI does that for a pool:
78
+ The accounts that register and transfer names need a balance on your Asset Hub for DotNS fees:
100
79
 
101
- ```sh
102
- bulletin-bootstrap --env <id> # authorize the default pool on an environment
103
- bulletin-bootstrap --env <id> --pool-size 20 # authorize a larger pool
104
- bulletin-bootstrap --mnemonic "<phrase>" # authorize a custom pool's accounts
105
- ```
80
+ - **Public testnets** — use the faucet (e.g. [faucet.polkadot.io](https://faucet.polkadot.io/)).
81
+ - **Restricted networks** fund the accounts out of band; there is no public faucet.
82
+
83
+ (These are the DotNS signing accounts on the Asset Hub — distinct from the Bulletin storage pool in step 2, which needs authorization, not funds.)
84
+
85
+ ## 5. Content gateway
86
+
87
+ Deployed content is content-addressed (a CID) on the Bulletin Chain and served over HTTP by the IPFS gateway you set as `ipfs` in step 1. The `.dot` name resolves to that CID via DotNS. Parity's public testnets resolve a deployed name at `https://<name>.dot.li`; your environment uses whatever gateway / resolver you point it at.
88
+
89
+ ## 6. Verify the setup
106
90
 
107
- It derives the pool accounts, authorizes each one for storage, and funds them. On a testnet the authorizer is the dev account (`//Alice`); on a production chain the network's own authorizer must grant authorization — `bulletin-bootstrap` cannot self-authorize there. It's a one-time setup step, not part of a routine deploy. See [`docs/bootstrap.md`](docs/bootstrap.md) for the full operator reference.
91
+ A **test deploy** is the real check run one (see the [README](README.md)) and confirm it completes and resolves. Two failures map straight back to the steps above:
108
92
 
109
- ## Signing modes, in short
93
+ - `not authorized to upload` → the storage account lacks authorization (step 2).
94
+ - a personhood / registration gate → the registering account needs PoP (step 3).
110
95
 
111
- - **Signed in (default):** a worker registers and uploads, then hands the name to your account zero wallet signatures on testnet.
112
- - **`--no-transfer-to-signedin-user`:** sign every DotNS transaction with your mobile session instead.
113
- - **`--mnemonic "..."` / `MNEMONIC`:** use a mnemonic instead of a session.
96
+ If you're working from a **clone of the repository** (not just the npm package), `tools/` has read-only diagnostics that take `--env <id>`: `check-bulletin-auth.mjs` (storage quotas), `check-balances.mjs` (balances + quota), `check-pop-status.mjs` (personhood), `probe-env-health.mjs` (RPC reachability). These ship with the repo, not the published package.
114
97
 
115
- ## Troubleshooting
98
+ A concrete, fully worked instance of this setup — for the E2E test environment — lives in [`docs/e2e-bootstrap.md`](docs/e2e-bootstrap.md).
116
99
 
117
- | Symptom | Cause / fix |
118
- |---|---|
119
- | `IPFS CLI not installed` | Install [Kubo](https://docs.ipfs.tech/install/), or add `--js-merkle`. |
120
- | Name rejected as reserved / too short | Use a base label of **6+ characters**. |
121
- | `Login session unavailable or expired` | Re-run `bulletin-deploy login`. |
122
- | Not authorized to upload | The storage account lacks a Bulletin `TransactionStorage` authorization. On `paseo-next-v2` the shared pool is already authorized; on your own setup, see [Bootstrapping your own storage pool](#bootstrapping-your-own-storage-pool). On a production chain, request authorization from the network's authorizer. |
100
+ ---
123
101
 
124
- For the full option reference, run `bulletin-deploy --help`.
102
+ Once the environment is set up, day-to-day use is in the [README](README.md): build your site, `bulletin-deploy ./dist myapp.dot`, done.
@@ -13,6 +13,7 @@ const flags = {};
13
13
  for (let i = 0; i < args.length; i++) {
14
14
  if (args[i] === "--pool-size") { flags.poolSize = parseInt(args[++i], 10); }
15
15
  else if (args[i] === "--mnemonic") { flags.mnemonic = args[++i]; }
16
+ else if (args[i] === "--authorizer") { flags.authorizer = args[++i]; }
16
17
  else if (args[i] === "--rpc") { flags.rpc = args[++i]; }
17
18
  else if (args[i] === "--env") { flags.env = args[++i]; }
18
19
  else if (args[i] === "--version" || args[i] === "-V") { flags.version = true; }
@@ -32,17 +33,27 @@ if (flags.help) {
32
33
  console.log(`bulletin-bootstrap v${VERSION}
33
34
 
34
35
  Usage:
35
- bulletin-bootstrap
36
+ bulletin-bootstrap [options]
37
+
38
+ Reports the authorization status of each pool account and, when an authorizer
39
+ key is available, grants authorization to any account that needs it.
40
+
41
+ The Bulletin chain has no fee model — storage access is gated by TransactionStorage
42
+ authorization quota, not account balance. This tool manages that quota.
36
43
 
37
44
  Options:
38
- --mnemonic "..." Pool root mnemonic (or set BULLETIN_POOL_MNEMONIC / MNEMONIC env var)
39
- --rpc wss://... Bulletin RPC (or set BULLETIN_RPC env var)
40
- --env <id> Load environment by id from environments.json (sets default RPC and V2 flag)
41
- --pool-size N Number of pool accounts to initialize (default: 10)
42
- --version Show version
43
- --help Show this help
44
-
45
- Initialize pool accounts for Bulletin storage authorization.`);
45
+ --mnemonic "..." Pool root mnemonic used to derive pool accounts
46
+ (also readable from BULLETIN_POOL_MNEMONIC or MNEMONIC env var).
47
+ Default: the well-known dev phrase (same key the deploy path uses).
48
+ --authorizer "..." Seed/mnemonic of the key that holds authorization authority
49
+ on this chain (e.g. "//Alice", a full mnemonic, or a hex seed).
50
+ On testnets, defaults to //Alice if omitted.
51
+ On non-testnets, required to grant — omit to get status only.
52
+ --rpc wss://... Bulletin RPC endpoint (or set BULLETIN_RPC env var)
53
+ --env <id> Load environment by id from environments.json
54
+ --pool-size N Number of pool accounts to check/initialize (default: 10)
55
+ --version Show version
56
+ --help Show this help`);
46
57
  process.exit(0);
47
58
  }
48
59
 
@@ -52,6 +63,10 @@ const mnemonic = flags.mnemonic ?? process.env.BULLETIN_POOL_MNEMONIC ?? process
52
63
 
53
64
  const bootstrapOpts = {};
54
65
 
66
+ if (flags.authorizer) {
67
+ bootstrapOpts.authorizerMnemonic = flags.authorizer;
68
+ }
69
+
55
70
  if (flags.env) {
56
71
  const envJsonPath = fileURLToPath(new URL("../assets/environments.json", import.meta.url));
57
72
  let envDoc;
@@ -8,12 +8,13 @@ import {
8
8
  requestResourceAllocation,
9
9
  resolveSigner,
10
10
  summarizeOutcomes
11
- } from "../chunk-5OKB3TEB.js";
11
+ } from "../chunk-5FLTDWWP.js";
12
12
  import {
13
13
  renderLoginStatus,
14
14
  renderLogoutStatus,
15
15
  renderQrCode
16
16
  } from "../chunk-RIRDBSBG.js";
17
+ import "../chunk-TSPERKUS.js";
17
18
  import "../chunk-ZOC4GITL.js";
18
19
  export {
19
20
  BULLETIN_RESOURCE,
@@ -12,7 +12,8 @@ import {
12
12
  resolveSigner,
13
13
  sessionRootPublicKey,
14
14
  summarizeOutcomes
15
- } from "../../chunk-5OKB3TEB.js";
15
+ } from "../../chunk-5FLTDWWP.js";
16
+ import "../../chunk-TSPERKUS.js";
16
17
  import "../../chunk-ZOC4GITL.js";
17
18
  export {
18
19
  BULLETIN_RESOURCE,
@@ -9,9 +9,10 @@ import {
9
9
  getPeopleChainEndpoints,
10
10
  hasPersistedSession,
11
11
  resolveBulletinEndpoints
12
- } from "./chunk-7OOKA4BJ.js";
13
- import "./chunk-DFRIBV6O.js";
14
- import "./chunk-KY3CRS66.js";
12
+ } from "./chunk-F2SDBU57.js";
13
+ import "./chunk-TSPERKUS.js";
14
+ import "./chunk-ORIB2S5P.js";
15
+ import "./chunk-V4RTUMBT.js";
15
16
  import "./chunk-QRKI6MMK.js";
16
17
  import "./chunk-ZOC4GITL.js";
17
18
  export {
@@ -9,10 +9,10 @@ import {
9
9
  offerBugReport,
10
10
  scrubSecrets,
11
11
  setDeployContext
12
- } from "./chunk-D2WW2CIA.js";
13
- import "./chunk-ZG56UL3V.js";
14
- import "./chunk-DFRIBV6O.js";
15
- import "./chunk-KY3CRS66.js";
12
+ } from "./chunk-OR7LUTET.js";
13
+ import "./chunk-ZC43ORKF.js";
14
+ import "./chunk-ORIB2S5P.js";
15
+ import "./chunk-V4RTUMBT.js";
16
16
  export {
17
17
  buildCliFlagsSummary,
18
18
  buildLabels,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  preflightSssAllowance
3
- } from "./chunk-IF6BNIKT.js";
3
+ } from "./chunk-SNDIOANW.js";
4
4
  import {
5
5
  statementSigningAccount
6
6
  } from "./chunk-GRPLHUYC.js";
@@ -34,16 +34,19 @@ import {
34
34
  STALE_SESSION_MESSAGE,
35
35
  getPeopleChainEndpoints,
36
36
  hasPersistedSession
37
- } from "./chunk-7OOKA4BJ.js";
37
+ } from "./chunk-F2SDBU57.js";
38
38
  import {
39
39
  setDeployContext
40
- } from "./chunk-D2WW2CIA.js";
40
+ } from "./chunk-OR7LUTET.js";
41
41
  import {
42
42
  probeChunks
43
- } from "./chunk-UBLIZRYW.js";
43
+ } from "./chunk-7HQL3TPT.js";
44
44
  import {
45
45
  packSection
46
46
  } from "./chunk-C2TS5MER.js";
47
+ import {
48
+ CLI_NAME
49
+ } from "./chunk-TSPERKUS.js";
47
50
  import {
48
51
  DotNS,
49
52
  PUBLISHER_ABI,
@@ -53,7 +56,7 @@ import {
53
56
  parseDomainName,
54
57
  popStatusName,
55
58
  verifyNonceAdvanced
56
- } from "./chunk-FAB6BWDB.js";
59
+ } from "./chunk-ARPBPQZQ.js";
57
60
  import {
58
61
  derivePoolAccounts,
59
62
  detectTestnet,
@@ -61,7 +64,7 @@ import {
61
64
  fetchPoolAuthorizations,
62
65
  isAuthorizationSufficient,
63
66
  selectAccount
64
- } from "./chunk-4PVJ2JBZ.js";
67
+ } from "./chunk-4IUTMHVB.js";
65
68
  import {
66
69
  VERSION,
67
70
  captureWarning,
@@ -75,7 +78,7 @@ import {
75
78
  truncateAddress,
76
79
  withDeploySpan,
77
80
  withSpan
78
- } from "./chunk-DFRIBV6O.js";
81
+ } from "./chunk-ORIB2S5P.js";
79
82
  import {
80
83
  DEFAULT_ENV_ID,
81
84
  getPopSelfServeConfig,
@@ -417,7 +420,9 @@ function isBenignTeardownError(error) {
417
420
  const s = error instanceof Error ? `${error.name ?? ""} ${error.message ?? ""}` : String(error);
418
421
  return /DestroyedError|Client destroyed/.test(s);
419
422
  }
420
- var CID_CONFIG = { version: 1, codec: 85, hashCode: 18, hashLength: 32 };
423
+ var SHA256_MULTIHASH_CODE = 18;
424
+ var BLAKE2B_256_MULTIHASH_CODE = 45600;
425
+ var CID_CONFIG = { version: 1, codec: 85, hashCode: SHA256_MULTIHASH_CODE, hashLength: 32 };
421
426
  function deriveRootSigner(mnemonic, path3 = "") {
422
427
  const entropy = mnemonicToEntropy(mnemonic);
423
428
  const miniSecret = entropyToMiniSecret(entropy);
@@ -593,6 +598,9 @@ function chooseSignerInput(opts) {
593
598
  function isPhoneSignerActive(options) {
594
599
  return !!(options.signer && options.signerAddress && !options.transferTo);
595
600
  }
601
+ function shouldHandoverName(opts) {
602
+ return !!opts.transferTo && opts.registeredFresh;
603
+ }
596
604
  function formatStorageSignerLine(slotAddress, failReason) {
597
605
  if (slotAddress) return ` Storage signer: allowance slot ${slotAddress}`;
598
606
  return ` Storage signer: pool fallback (${failReason ?? "no session"})`;
@@ -616,7 +624,7 @@ function selectStorageReconnect(options) {
616
624
  console.warn(
617
625
  `\u26A0 Bulletin allowance slot not usable: ${reason}
618
626
  Falling back to the shared pool account for storage (fine on testnet).
619
- To use your own allowance, run: bulletin-deploy logout && bulletin-deploy login`
627
+ To use your own allowance, run: ${CLI_NAME} logout && ${CLI_NAME} login`
620
628
  );
621
629
  return getProvider();
622
630
  }
@@ -690,9 +698,8 @@ function watchTransaction(tx, signer, txOpts, onSuccess, { label = "transaction"
690
698
  });
691
699
  }
692
700
  async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58, opts = {}) {
693
- const hashCode = 18;
694
- const cid = createCID(chunkBytes, CID_CONFIG.codec, hashCode);
695
- const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(hashCode) }, data: chunkBytes });
701
+ const cid = createCID(chunkBytes, CID_CONFIG.codec, CID_CONFIG.hashCode);
702
+ const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(CID_CONFIG.hashCode) }, data: chunkBytes });
696
703
  const txOpts = { mortality: { mortal: true, period: CHUNK_MORTALITY_PERIOD }, nonce };
697
704
  const { value, viaFallback, receipt } = await watchTransaction(tx, signer, txOpts, () => {
698
705
  console.log(` CID: ${cid.toString()}`);
@@ -700,11 +707,15 @@ async function storeChunk(unsafeApi, signer, chunkBytes, nonce, ss58, opts = {})
700
707
  }, { label: `chunk(nonce:${nonce})`, rpc: BULLETIN_ENDPOINTS, senderSS58: ss58, expectedNonce: nonce, timeoutMs: CHUNK_TIMEOUT_MS, fetchNonce: opts.fetchNonce });
701
708
  return { ...value, viaFallback, receipt };
702
709
  }
703
- async function storeFile(contentBytes, { client: existingClient, unsafeApi: existingApi, signer: existingSigner } = {}) {
710
+ async function storeFile(contentBytes, {
711
+ client: existingClient,
712
+ unsafeApi: existingApi,
713
+ signer: existingSigner,
714
+ hashCode = CID_CONFIG.hashCode
715
+ } = {}) {
704
716
  console.log(`
705
717
  Size: ${(contentBytes.length / 1024).toFixed(2)} KB`);
706
718
  if (contentBytes.length > MAX_FILE_SIZE) throw new Error(`File exceeds 8MB limit. Use chunked deployment.`);
707
- const hashCode = 18;
708
719
  const cid = createCID(contentBytes, CID_CONFIG.codec, hashCode);
709
720
  console.log(` CID: ${cid.toString()}`);
710
721
  let client, unsafeApi, signer;
@@ -719,6 +730,13 @@ async function storeFile(contentBytes, { client: existingClient, unsafeApi: exis
719
730
  signer = provider.signer;
720
731
  }
721
732
  try {
733
+ const [probe] = await probeChunks([cid.toString()], { client });
734
+ if (probe.present === true) {
735
+ console.log(` Already on chain (block ${probe.block}, index ${probe.index}) \u2014 skipping upload.
736
+ `);
737
+ if (!existingClient) client.destroy();
738
+ return cid.toString();
739
+ }
722
740
  const tx = unsafeApi.tx.TransactionStorage.store_with_cid_config({ cid: { codec: BigInt(CID_CONFIG.codec), hashing: toHashingEnum(hashCode) }, data: contentBytes });
723
741
  const txOpts = { mortality: { mortal: true, period: 256 } };
724
742
  console.log(` Submitting...`);
@@ -1996,7 +2014,7 @@ async function deploy(content, domainName = null, options = {}) {
1996
2014
  if (options.suri) throw e;
1997
2015
  if (e?.name === "SignerNotAvailableError") {
1998
2016
  if (hasSession) console.error(STALE_SESSION_MESSAGE);
1999
- else console.log(" Login session unavailable or expired \u2014 falling back to pool. Run `bulletin-deploy login` to use your identity.");
2017
+ else console.log(` Login session unavailable or expired \u2014 falling back to pool. Run \`${CLI_NAME} login\` to use your identity.`);
2000
2018
  } else {
2001
2019
  throw e;
2002
2020
  }
@@ -2012,7 +2030,7 @@ async function deploy(content, domainName = null, options = {}) {
2012
2030
  const allowed = await preflightSssAllowance(statementAccount, () => getPeopleChainEndpoints(envId));
2013
2031
  if (allowed === false) {
2014
2032
  throw new NonRetryableError(
2015
- "Session signing allowance has expired (~2-3 days after login). Run `bulletin-deploy logout`, then `bulletin-deploy login`, to renew (login alone won't refresh a stale session)."
2033
+ `Session signing allowance has expired (~2-3 days after login). Run \`${CLI_NAME} logout\`, then \`${CLI_NAME} login\`, to renew (login alone won't refresh a stale session).`
2016
2034
  );
2017
2035
  }
2018
2036
  } catch (e) {
@@ -2394,6 +2412,7 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2394
2412
  ...phoneSignerActive ? { onPhoneSigningRequired: (label) => console.log(`
2395
2413
  Check your phone \u2192 ${label}`) } : {}
2396
2414
  });
2415
+ let registeredFresh = false;
2397
2416
  if (parsed?.isSubdomain) {
2398
2417
  const { owned, owner } = await dotns.checkSubdomainOwnership(parsed.sublabel, parsed.parentLabel);
2399
2418
  if (owned) {
@@ -2405,6 +2424,7 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2405
2424
  if (!parentOwnership.owned) throw new Error(`You must own ${parsed.parentLabel}.dot to register subdomains under it`);
2406
2425
  console.log(` Status: Registering subdomain...`);
2407
2426
  await dotns.registerSubdomain(parsed.sublabel, parsed.parentLabel);
2427
+ registeredFresh = true;
2408
2428
  }
2409
2429
  } else {
2410
2430
  const { owned } = await dotns.checkOwnership(name);
@@ -2413,6 +2433,7 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2413
2433
  } else {
2414
2434
  console.log(` Status: Registering...`);
2415
2435
  await dotns.register(name);
2436
+ registeredFresh = true;
2416
2437
  }
2417
2438
  }
2418
2439
  const contenthashHex = `0x${encodeContenthash(cid)}`;
@@ -2422,7 +2443,12 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2422
2443
  await publish(dotns, parsed, options.failOnPublishError);
2423
2444
  }
2424
2445
  }
2425
- if (options.transferTo) {
2446
+ if (options.transferTo && !registeredFresh) {
2447
+ console.log(` ${name}.dot already existed \u2014 updated content only; ownership unchanged (not transferred to ${options.transferTo}).`);
2448
+ console.log(` If you meant to claim it, run: ${CLI_NAME} transfer ${name} --env ${envId}${options.suri ? ` --mnemonic "<your worker key>"` : ""}`);
2449
+ setDeployAttribute("deploy.transfer.status", "skipped-existing");
2450
+ }
2451
+ if (shouldHandoverName({ transferTo: options.transferTo, registeredFresh })) {
2426
2452
  const transferTo = options.transferTo;
2427
2453
  await withSpan("deploy.transfer", `3. transfer ${name}.dot`, { "deploy.transfer.to": transferTo }, async () => {
2428
2454
  setDeployAttribute("deploy.transfer.worker", truncateAddress(options.signerAddress ?? ""));
@@ -2434,7 +2460,7 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2434
2460
  console.log(` Handed ${name}.dot to ${transferTo} (${transferRes.status}${transferRes.txHash ? `, tx ${transferRes.txHash}` : ""}).`);
2435
2461
  } catch (e) {
2436
2462
  setDeployAttribute("deploy.transfer.status", "failed");
2437
- const recover = `bulletin-deploy transfer ${name} --env ${envId}` + (options.suri ? ` --mnemonic "<your worker key>"` : "");
2463
+ const recover = `${CLI_NAME} transfer ${name} --env ${envId}` + (options.suri ? ` --mnemonic "<your worker key>"` : "");
2438
2464
  try {
2439
2465
  dotns.disconnect();
2440
2466
  } catch {
@@ -2497,9 +2523,9 @@ Have your phone ready \u2014 1 signature needed (link content)`);
2497
2523
  console.log("\n" + "=".repeat(60));
2498
2524
  console.log("DEPLOYMENT COMPLETE!");
2499
2525
  console.log("=".repeat(60));
2500
- console.log("\nPolkadot Triangle");
2501
- console.log(` - Polkadot Desktop: ${name}.dot`);
2502
- console.log(` - Polkadot Browser: ${browserUrlFor(name, envId)}`);
2526
+ console.log("\nCheck it out here:");
2527
+ console.log(` ${browserUrlFor(name, envId)}`);
2528
+ console.log(` ${name}.dot (in a Polkadot-aware browser)`);
2503
2529
  console.log("\n" + "=".repeat(60) + "\n");
2504
2530
  return { domainName: name, fullDomain: `${name}.dot`, cid, ipfsCid };
2505
2531
  } finally {
@@ -2938,6 +2964,8 @@ export {
2938
2964
  retryBudgetExhausted,
2939
2965
  isConnectionError,
2940
2966
  isBenignTeardownError,
2967
+ SHA256_MULTIHASH_CODE,
2968
+ BLAKE2B_256_MULTIHASH_CODE,
2941
2969
  deriveRootSigner,
2942
2970
  createCID,
2943
2971
  encodeContenthash,
@@ -2951,6 +2979,7 @@ export {
2951
2979
  __selectStorageProviderModeForTest,
2952
2980
  chooseSignerInput,
2953
2981
  isPhoneSignerActive,
2982
+ shouldHandoverName,
2954
2983
  formatStorageSignerLine,
2955
2984
  storeFile,
2956
2985
  __assignDenseNoncesForTest,