Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tld-aware-bulletin-deploy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"playground-cli": minor
---

Support per-network DotNS TLDs (`.paseo` on paseo-next-v2) and move the deploy library to `bulletin-deploy@0.15.0` (the renamed `@parity/polkadot-app-deploy`), which carries the post-wipe DotNS contract addresses. Deploys, decentralize, mod lookups, and all UI copy now use the environment's TLD; a name typed with the wrong TLD (e.g. `my-app.dot` on paseo-next-v2) is rejected with an actionable message. The `playground.dot` product id is unchanged by convention. The faucet links now use the correct `?parachain=1500` form (the previous `?network=pah` form dripped to the public Paseo Asset Hub, para 1000, instead of this network's chain).
8 changes: 4 additions & 4 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
retry_wait_seconds: 30
command: pnpm exec vitest run --config e2e/vitest.config.ts ${{ matrix.testFile || '' }} -t "${{ matrix.pattern }}"
env:
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.paseo
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
DOT_DEPLOY_VERBOSE: "1"
DOT_TAG: ${{ steps.setup.outputs.tag }}
Expand Down Expand Up @@ -164,7 +164,7 @@ jobs:
retry_wait_seconds: 30
command: pnpm exec vitest run --config e2e/vitest.config.ts ${{ matrix.testFile || '' }} -t "${{ matrix.pattern }}"
env:
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.paseo
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
DOT_DEPLOY_VERBOSE: "1"
DOT_TAG: ${{ steps.setup.outputs.tag }}
Expand Down Expand Up @@ -228,7 +228,7 @@ jobs:
retry_wait_seconds: 30
command: pnpm exec vitest run --config e2e/vitest.config.ts ${{ matrix.testFile || '' }} -t "${{ matrix.pattern }}"
env:
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.paseo
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
DOT_DEPLOY_VERBOSE: "1"
DOT_TAG: ${{ steps.setup.outputs.tag }}
Expand Down Expand Up @@ -313,7 +313,7 @@ jobs:
retry_wait_seconds: 30
command: pnpm exec vitest run --config e2e/vitest.config.ts ${{ matrix.testFile || '' }} -t "${{ matrix.pattern }}"
env:
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.dot
TEST_TEMPLATE_DOMAIN: dot-cli-mod-fixture.paseo
TEST_TEMPLATE_REPO: https://github.com/paritytech/Rock-Paper-Scissors
DOT_DEPLOY_VERBOSE: "1"
DOT_TAG: ${{ steps.setup.outputs.tag }}
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/funder-balance-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ name: Funder Balance Check
# Scheduled probe for the dedicated PAS funder on Paseo Asset Hub. When the
# balance falls below the threshold, the workflow opens (or comments on)
# a GitHub issue labelled `funder-low-balance` so someone can top it up from
# https://faucet.polkadot.io/. Idempotent — never files a second open issue.
# https://faucet.polkadot.io/?parachain=1500 (Asset Hub Next v2 — NOT the
# network=pah / para-1000 public Asset Hub). Idempotent — never files a
# second open issue.

on:
schedule:
Expand Down Expand Up @@ -68,7 +70,9 @@ jobs:
ADDRESS=$(grep '^address=' balance.txt | cut -d= -f2- || echo "unknown")
BALANCE=$(grep '^balance=' balance.txt | cut -d= -f2- || echo "unknown")
THRESHOLD=$(grep '^threshold=' balance.txt | cut -d= -f2- || echo "5000.00 PAS")
FAUCET="https://faucet.polkadot.io/?network=pah&address=${ADDRESS}"
# parachain=1500 targets Asset Hub Next v2, where the funder lives.
# network=pah drips to the PUBLIC Paseo Asset Hub (para 1000) instead.
FAUCET="https://faucet.polkadot.io/?parachain=1500&address=${ADDRESS}"
NOW=$(date -u +%FT%TZ)

# `.[0].number // empty` turns an empty result into no
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,23 @@ Flags:

### `playground deploy`

Builds the project, uploads the output to Bulletin, registers a `.dot` domain via DotNS, and optionally publishes the app to the Playground registry (so it shows up in the user's "my apps" list).
Builds the project, uploads the output to Bulletin, registers a DotNS domain under the environment's TLD (`.paseo` on paseo-next-v2), and optionally publishes the app to the Playground registry (so it shows up in the user's "my apps" list).

Flags:

- `--signer <mode>` — `dev` (fast, uses shared dev keys for upload + DotNS — 0 or 1 phone approval) or `phone` (signs DotNS + publish with your logged-in account — 3 or 4 phone approvals). Interactive prompt if omitted.
- `--domain <name>` — DotNS label (with or without the `.dot` suffix). Interactive prompt if omitted.
- `--domain <name>` — DotNS label (with or without the environment's TLD suffix, e.g. `.paseo`). Interactive prompt if omitted.
- `--buildDir <path>` — directory holding the built artifacts (default `dist/`). Interactive prompt if omitted.
- `--no-build` — skip the frontend build step and deploy whatever is already in `--buildDir`.
- `--playground` — publish to the playground registry so the app appears under "my apps". Interactive prompt (default: no) if omitted.
- `--private` — publish to the playground with private (owner-only) visibility. Requires `--playground`. Not interactively prompted; pass the flag to opt in.
- `--moddable` / `--no-moddable` — publish the source repo URL alongside the deploy so others can `playground mod` it. Requires `--playground`. Interactive prompt (default: no) if omitted. The CLI reads your existing `origin` and records its URL in the Bulletin metadata; it never creates a repo or pushes for you. The deploy fails with an actionable message if `origin` is unset, points to a private repo, or points to anything other than GitHub (since `playground mod` only fetches from `codeload.github.com`). Set up the repo yourself before re-running: create a public repo on GitHub, then `git remote add origin https://github.com/<user>/<repo>` followed by `git push -u origin main`. (If you happen to have `gh` installed, `gh repo create my-app --public --source=. --push` does both in one shot — `playground` does not require `gh`.)
- `--suri <suri>` — override signer with a dev secret URI (e.g. `//Alice`). Useful for CI.
- `--env <env>` — target environment. Defaults to `paseo-next-v2` (the only one fully wired today). Accepts the polkadot-app-deploy env IDs (`preview`, `paseo-next`, `paseo-review`, `paseo-next-v2`, `polkadot`, `kusama`) plus the legacy `testnet`/`mainnet` aliases — `testnet` maps to `paseo-next-v2`, `mainnet` to `polkadot`. Any env other than `paseo-next-v2` throws "not supported" until its entry is wired up in `src/config.ts::CONFIGS`.
- `--env <env>` — target environment. Defaults to `paseo-next-v2` (the only one fully wired today). Accepts the bulletin-deploy env IDs (`preview`, `paseo-next`, `paseo-review`, `paseo-next-v2`, `polkadot`, `kusama`) plus the legacy `testnet`/`mainnet` aliases — `testnet` maps to `paseo-next-v2`, `mainnet` to `polkadot`. Any env other than `paseo-next-v2` throws "not supported" until its entry is wired up in `src/config.ts::CONFIGS`.

Passing all four of `--signer`, `--domain`, `--buildDir`, and `--playground` runs in fully non-interactive mode. Any absent flag is filled in by the TUI prompt. `--moddable` and `--private` are independently optional in both modes — their absence means a non-moddable, public deploy.

**Requirement**: the `ipfs` CLI (Kubo) must be on `PATH`. `playground login` installs it; if you skipped login you can install it manually (`brew install ipfs` or follow [docs.ipfs.tech/install](https://docs.ipfs.tech/install/)). This is a temporary requirement while `polkadot-app-deploy`'s pure-JS merkleizer has a bug that makes the browser fallback unusable.
**Requirement**: the `ipfs` CLI (Kubo) must be on `PATH`. `playground login` installs it; if you skipped login you can install it manually (`brew install ipfs` or follow [docs.ipfs.tech/install](https://docs.ipfs.tech/install/)). This is a temporary requirement while `bulletin-deploy`'s pure-JS merkleizer has a bug that makes the browser fallback unusable.

The publish step is always signed by the user so the registry contract records their address as the app owner — this is what drives the Playground "my apps" view.

Expand All @@ -106,7 +106,7 @@ For fully non-interactive (CI) runs, combine `--signer`, `--domain`, `--buildDir

### `playground deploy-all`

Deploy several `.dot` apps in a single invocation. Builds run in parallel; **all on-chain work (Bulletin upload, DotNS, and the playground publish) is serialized per signer account** so concurrent deploys that share a signer never collide on a nonce. Because every app uses one shared signer (typically `--signer dev`), the on-chain phases run strictly one app at a time and only the builds overlap. This is the batch counterpart to `playground deploy`; the single-app command is unchanged.
Deploy several DotNS apps in a single invocation. Builds run in parallel; **all on-chain work (Bulletin upload, DotNS, and the playground publish) is serialized per signer account** so concurrent deploys that share a signer never collide on a nonce. Because every app uses one shared signer (typically `--signer dev`), the on-chain phases run strictly one app at a time and only the builds overlap. This is the batch counterpart to `playground deploy`; the single-app command is unchanged.

The command is non-interactive by design (N concurrent Ink TUIs are unreadable). Apps are listed in a JSON manifest; shared options come from flags and apply to every app.

Expand Down Expand Up @@ -144,15 +144,15 @@ Every deploy extrinsic (DotNS register/`setContenthash`, Bulletin chunk `store`,

### `playground decentralize`

Take an existing static site — either a live URL to mirror or a local build directory — upload it to Polkadot Bulletin, and register a `.dot` name pointing at it. This is the "decentralize a site I already have" counterpart to `playground deploy` (which builds your project first).
Take an existing static site — either a live URL to mirror or a local build directory — upload it to Polkadot Bulletin, and register a DotNS name (with the environment's TLD, e.g. `my-site.paseo`) pointing at it. This is the "decentralize a site I already have" counterpart to `playground deploy` (which builds your project first).

Provide `--site` or `--path` to run headless; omit both to launch the interactive TUI (source → URL/path → signer → domain → publish? → moddable?).

Flags:

- `--site <url>` — URL of a live static site to clone (http/https).
- `--path <dir>` — a local directory of built static files (e.g. `./dist`). Conflicts with `--site`.
- `--dot <name>` — DotNS domain (with or without the `.dot` suffix). Omit to auto-generate a free random name.
- `--dot <name>` — DotNS domain (with or without the environment's TLD suffix). Omit to auto-generate a free random name.
- `--suri <suri>` — sign with this SURI (a dev name like `//Bob`, or a BIP-39 mnemonic). Defaults to the session signer paired by `playground login`.
- `--playground` — after upload, also publish a minimal entry to the playground registry so the app appears in the playground-app's Apps tab. Off by default.
- `--tag <tag>` — category tag for the published app so people can filter for it. Requires `--playground`.
Expand All @@ -178,7 +178,7 @@ The implementation is GitHub-only and **requires no CLI tooling** — neither `g

Flags:

- `[domain]` — positional; interactive picker over the registry if omitted. `.dot` suffix optional. The picker is filtered to moddable apps only.
- `[domain]` — positional; interactive picker over the registry if omitted. TLD suffix optional. The picker is filtered to moddable apps only.
- `--suri <suri>` — dev signer secret URI (e.g. `//Alice`).

The local directory name is auto-generated as `<slug>-<6 hex chars>` so repeated mods of the same starter never collide (unlike GitHub forks, which were limited to one per account per repo).
Expand Down Expand Up @@ -209,7 +209,7 @@ DOT_MEMORY_TRACE=1 DOT_DEPLOY_VERBOSE=1 playground deploy ...
```

- `DOT_MEMORY_TRACE=1` streams a per-second `rss / heap / external / peak` sample to stderr from the watchdog worker. The worker has its own event loop, so samples keep firing even while the main thread is busy — perfect for capturing the timeline of a leak.
- `DOT_DEPLOY_VERBOSE=1` prefixes every `polkadot-app-deploy` log line with `[+<seconds>s]` so you can line the memory samples up with the exact chunk / retry / reconnect that preceded each spike.
- `DOT_DEPLOY_VERBOSE=1` prefixes every `bulletin-deploy` log line with `[+<seconds>s]` so you can line the memory samples up with the exact chunk / retry / reconnect that preceded each spike.

Attach the combined output to the bug report along with the site size and roughly how many chunks the deploy was into when the spike started — it's dramatically more useful than a stack trace alone.

Expand Down Expand Up @@ -310,7 +310,7 @@ The first two are also enforced in CI; running them locally catches the failure
- The CDM contract packages are `@parity/cdm-*` (migrated from `@dotdm/*`, June 2026): `@parity/cdm-codegen` and `@parity/cdm-builder` are pinned EXACT (this line has shipped breaking changes in patch releases), `@parity/cdm-env` rides a caret. CI greps for `['"]@dotdm/` to block re-introduction; the legacy `@dotdm` `1.1.1` stable still pulled `@polkadot-apps/*` + `polkadot-api@1.x`.
- `@novasamatech/*` resolves transitively through `@parity/product-sdk-terminal@^0.3.2` (host-papp ≥ 0.8.6); there is no version override. Do NOT re-pin to host-papp 0.7.x or 0.8.5 — mobile-pairing compatibility is purely which host-papp version resolves (see CLAUDE.md). Two small local pnpm patches remain on `@novasamatech/statement-store` and `@novasamatech/sdk-statement`.
- `polkadot-api` is on `^2.1.x` and `@polkadot-api/sdk-ink` on `^0.7.0`. The lockfile contains a stale `polkadot-api@1.x` only because `@parity/dotns-cli`'s declared dep references it; that CLI ships as a single bundled `dist/cli.js` with all deps inlined, so the 1.x decl is never resolved at runtime. Effectively the runtime is PAPI 2.x-only.
- `polkadot-app-deploy` is pinned to an explicit version — not `latest`. Currently `0.8.3`. A previous `latest` (0.6.8) had a WebSocket heartbeat bug (40s default < 60s chunk timeout) that tore chunk uploads down as `WS halt (3)`; keeping the pin explicit avoids ever sliding back onto that. When bumping, check the release notes for any changes to `deploy()` / `DotNS` APIs we rely on (`jsMerkle`, `signer`, `signerAddress`, `storageSigner`, `storageSignerAddress`, `mnemonic`, `rpc`, `attributes`).
- `bulletin-deploy` (the renamed `@parity/polkadot-app-deploy`) is pinned to an explicit version — not `latest`. Currently `0.15.0`. A previous `latest` (0.6.8) had a WebSocket heartbeat bug (40s default < 60s chunk timeout) that tore chunk uploads down as `WS halt (3)`; keeping the pin explicit avoids ever sliding back onto that. When bumping, check the release notes for any changes to `deploy()` / `DotNS` APIs we rely on (`jsMerkle`, `signer`, `signerAddress`, `storageSigner`, `storageSignerAddress`, `mnemonic`, `rpc`, `attributes`) — the full bump protocol lives in CLAUDE.md's dependency-pins section.
- `pnpm.overrides` also redirects `@parity/dotns-cli`'s declared `@polkadot-api/descriptors` dep to `stubs/papi-descriptors-stub/`. `@parity/dotns-cli@0.6.1`'s published manifest references a workspace path (`file:.papi/descriptors`) that doesn't exist in the tarball; pnpm refuses, npm tolerates it. dotns-cli ships as a fully-bundled `dist/cli.js` so the stub (exporting `{}`) is functionally correct. Remove once `@parity/dotns-cli` republishes a clean manifest.

## Architecture Highlights
Expand All @@ -322,9 +322,9 @@ The first two are also enforced in CI; running them locally catches the failure
- **Session lifecycle** (`src/utils/auth.ts`) — `getSessionSigner()` returns an explicit `destroy()` handle. Callers MUST call it (typically from a `useEffect` cleanup) — the host-papp adapter keeps the Node event loop alive.
- **Deploy SDK / CLI split** (`src/utils/deploy/` + `src/commands/deploy/`) — the CLI command is a thin Commander + Ink wrapper around a pure `runDeploy()` orchestrator. The orchestrator avoids React/Ink so WebContainer consumers (e.g. RevX) can drive their own UI off the same event stream.
- **Signer-mode isolation** (`src/utils/deploy/signerMode.ts`) — decides which signer each deploy phase uses (pool mnemonic vs user's phone) in one place so the mainnet rewrite can be a single-file swap.
- **Bulletin delegation** — all storage-side hardening (pool management, chunk retry, nonce fallback, DAG-PB verification, DotNS commit-reveal) stays inside `polkadot-app-deploy`. `playground deploy` deliberately does NOT pass `jsMerkle: true` today: the pure-JS merkleizer drops DAG-PB blocks, so sites return 404. We rely on the Kubo binary path (`playground login` installs `ipfs`) until the upstream merkleizer is fixed, at which point `jsMerkle: true` flips back on for the WebContainer (RevX) story.
- **Bulletin delegation** — all storage-side hardening (pool management, chunk retry, nonce fallback, DAG-PB verification, DotNS commit-reveal) stays inside `bulletin-deploy`. `playground deploy` deliberately does NOT pass `jsMerkle: true` today: the pure-JS merkleizer drops DAG-PB blocks, so sites return 404. We rely on the Kubo binary path (`playground login` installs `ipfs`) until the upstream merkleizer is fixed, at which point `jsMerkle: true` flips back on for the WebContainer (RevX) story.
- **Signing proxy** (`src/utils/deploy/signingProxy.ts`) — wraps the user's `PolkadotSigner` to emit `sign-request`/`-complete`/`-error` lifecycle events. The TUI renders these as "📱 Check your phone" panels with live step counts.
- **Playground publish is ours** (`src/utils/deploy/playground.ts`) — we deliberately do NOT use `polkadot-app-deploy`'s `--playground` flag. We call the registry contract from `src/utils/registry.ts` with the user's signer so the contract records their `env::caller()` as the owner — required for the Playground app's "my apps" view.
- **Playground publish is ours** (`src/utils/deploy/playground.ts`) — we deliberately do NOT use `bulletin-deploy`'s `--playground` flag. We call the registry contract from `src/utils/registry.ts` with the user's signer so the contract records their `env::caller()` as the owner — required for the Playground app's "my apps" view.

## Security

Expand Down
8 changes: 4 additions & 4 deletions docs/e2e-bootstrap.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Two categories. All owned by SIGNER after bootstrap.

| Domain | Purpose |
|---|---|
| `dot-cli-mod-fixture.dot` | `dot mod` fixture — has `repository` metadata pointing at Rock-Paper-Scissors |
| `dot-cli-mod-fixture.paseo` | `dot mod` fixture — has `repository` metadata pointing at Rock-Paper-Scissors |
| `e2e-cli-foundry` | `pr-deploy-foundry` / `nightly-deploy-foundry` cell |
| `e2e-cli-cdm` | `pr-deploy-cdm` cell (currently skipped pending fixture upgrade) |
| `e2e-cli-hardhat` | `nightly-deploy-hardhat` cell |
Expand Down Expand Up @@ -138,17 +138,17 @@ bun tools/register-e2e-fixtures.ts
```bash
bun tools/register-e2e-fixtures.ts --domain <name>
# e.g.:
bun tools/register-e2e-fixtures.ts --domain dot-cli-mod-fixture.dot
bun tools/register-e2e-fixtures.ts --domain dot-cli-mod-fixture.paseo
bun tools/register-e2e-fixtures.ts --domain e2e-cli-foundry
```

### `dot-cli-mod-fixture.dot` repository URL changed
### `dot-cli-mod-fixture.paseo` repository URL changed

Edit `FIXTURES` in `tools/register-e2e-fixtures.ts` (the `repositoryUrl` field)
and re-run:

```bash
bun tools/register-e2e-fixtures.ts --domain dot-cli-mod-fixture.dot
bun tools/register-e2e-fixtures.ts --domain dot-cli-mod-fixture.paseo
```

Also update `TEST_TEMPLATE_REPO` in `.github/workflows/e2e.yml` and `e2e/cli/helpers/dot.ts`.
Expand Down
Loading
Loading