Build & publish an app¶
This guide takes a first Product from a Product SDK template to a live app. You build a static bundle, register a .dot domain and publish with the pad deploy CLI. When you are done, the app is reachable as <name>.dot inside the Polkadot app and at https://<name>.dev-dot.li on the web gateway.
pad uploads your bundle to the Bulletin Chain and points your .dot domain at the result. The gateway then resolves that name in the browser and renders the bundle. App delivery describes the whole pipeline.
Paseo Next v2
This page covers the PCF Products Devnet (devnet preset). For Paseo Next v2 (paseo preset), see Deploy Your App.
Prerequisites¶
pad requires Node.js 22 or newer. On Node 20 it fails at startup with an unrelated-looking error (a module SyntaxError, or Missing WebSocket class), so check with node --version before installing.
npm i -g @polkadot-community-foundation/dotns-cli@latest # dotns
npm i -g @polkadot-community-foundation/polkadot-app-deploy@latest # pad, pad-bootstrap
Install only what you need: pad publishes (and can register the name for you), and dotns manages names in depth. Both are large. Expect a few minutes and roughly 1.5 GB across the global prefix and the npm cache, with no progress output while npm works.
Always take the latest release. These CLIs carry the current contract addresses and network presets inside them, so an older copy can talk to a retired deployment and report success while nothing changes. To upgrade one you already have, re-run the same command.
On some ARM64 platforms (for example Termux on Android), a transitive dependency, node-datachannel, ships no prebuilt binary, and its native build fails during npm i -g. It is only used for optional peer-to-peer CID verification, so skip the build with --ignore-scripts:
In this mode only the peer-to-peer verification path is unavailable. The CLI's gateway-based CID verification still works.
Every command below targets the Devnet with --env devnet. pad already defaults to devnet, but pass the flag every time anyway, so a command never lands on another network by accident.
Your signing account needs three things before the first on-chain step:
- Native tokens on Asset Hub for fees. See Faucet.
- A mapped EVM address.
dotnsandpadboth sign PolkaVM transactions on Asset Hub, and an unmapped account fails on its first one. Map it once withdotns account map --env devnet. - A Bulletin storage authorization, so it can upload your bundle. See below.
You also need to own the .dot name you publish to. Step 3 covers that.
Get storage authorization¶
Publishing writes your app to the Bulletin Chain. Bulletin storage works by authorization, not by fees: before pad can upload, the uploading account needs a live authorization, which is an on-chain quota of bytes and transactions. You don't pay devnet tokens per upload, and pad never authorizes itself. Without an authorization it stops before uploading anything.
The account that signs is the account that uploads. When you pass --mnemonic, pad uploads from that same account, so that is the account to authorize: the one that owns your .dot name.
To grant it, open the Faucet tab of the Bulletin Chain Console, select the Products Devnet network and authorize the SS58 address you deploy with. The same page lists current authorizations and when each expires.
This Storage Faucet is not the token faucet. The token faucet gives you native devnet tokens for transaction fees. The Storage Faucet grants a Bulletin storage allowance. Publishing needs both: tokens to sign the on-chain transactions, and an allowance to upload the bundle.
Authorizations are finite and expire. If a deploy that used to work stops at the upload step, the allowance has most likely lapsed. Grant it again and re-run.
Authorize from the CLI¶
dotns can also grant the authorization directly, signed by the shared devnet authorizer:
This draws on a shared authorizer budget, so request only what you need. 1000 transactions and 100 MiB comfortably cover a typical app. Raising --transactions or --bytes past the shared budget fails with InsufficientAuthorizerBudget. Use the Storage Faucet above for larger grants.
Deploying without your own key¶
pad can also upload from a shared pool of helper accounts, which it uses when you deploy without --mnemonic. Those accounts are pre-authorized on the Devnet, and pad-bootstrap (same package, no extra install) tops them up:
Pass --authorizer explicitly. Without it, pad-bootstrap falls back to //Alice, which cannot grant here. This path does not authorize your own account, so it is only useful for CI and shared setups. A deploy that signs with --mnemonic still needs the authorization above.
1. Scaffold with the Product SDK¶
Start from a template rather than an empty directory. The playground-app-template is a minimal React + Vite + TypeScript app on Product SDK 0.39.0, so it speaks the hosts' TrUAPI version. It shows the app-scoped product account (SS58 and H160 addresses) and signs a message via @parity/product-sdk-signer.
Your app runs as a Product inside the Polkadot host (mobile, desktop or the web gateway). The host lends it a signer and routes all chain RPC. You reach the platform through @parity/product-sdk: createApp(config) returns an app with wallet, chain and cloud-storage APIs, and the SDK also has contract and identity modules. See Use platform services from the SDK.
2. Build the frontend¶
Produce a static build directory. With a Vite template:
The output (dist/) is what you publish.
3. Register a .dot domain¶
Names are .dot domains managed by DotNS. Check that the name is free and register it with the DotNS CLI:
Pass the label without the .dot suffix: my-cool-app, not my-cool-app.dot.
Pick a label whose stem is nine characters or longer, so it registers without a personhood check. Shorter labels are gated. Registration is a commit-reveal handshake that takes a few minutes and costs a deposit. Register a .dot domain has the rules and the verification step.
You can skip this step if you deploy with the account that will hold the name. pad registers the name for you during deploy if your signing account does not already own it.
4. Publish with pad¶
Point pad at your build directory and the target name. pad takes its signer explicitly. It does not read the dotns keystore:
Keep the phrase in an environment variable, and never commit or print it.
pad can also sign in with your Polkadot app: run pad login and pair your phone. While you are signed in, pad registers and deploys with a local key, then hands the name to your app account, so your phone signs nothing. These guides use --mnemonic throughout.
pad uploads the bundle to Bulletin (skipping unchanged blocks on repeat deploys), registers the name if needed and writes the content hash on Asset Hub. A successful run ends with Verified on-chain: and the CID it published.
Check that the name points at that CID before you share it:
The app is now live at <name>.dot in the Polkadot app and at https://<name>.dev-dot.li on the web gateway.
Add card metadata¶
Browse and the Polkadot app show your Product using a product config that pad reads at deploy time. Without one, the app still works, but it has no name, description or icon to show.
Save this as polkadot-app-deploy.config.ts in your project. pad walks up from the build directory to find it:
export default {
domain: "my-cool-app.dot",
displayName: "My Cool App",
description: "What it does, in one line.",
icon: { path: "./icon.png", format: "png" },
executables: [{ kind: "app", path: "./dist", appVersion: [1, 0, 0] }],
};
Paths are relative to the config file, and the icon must exist. pad checks all of this before it uploads anything. It then uploads the icon and writes the manifest record on your name, plus an executable record on app.<name>.dot:
Icon CID: bafk2bzacecqdzkvdzflim3gspebc2iaqfjxtmqbuo6lpaqmvmlfhvkqxl2klw
Writing root manifest text record on my-cool-app.dot (177 B)…
✓ 2 text records written.
executables also accepts widget and worker entries, published to the widget. and worker. subnames.
Reference apps¶
These small apps are deployed on the Devnet. Their source is in the browse repository:
| App | Open | .dot name | What it does |
|---|---|---|---|
| Calculator | https://calculator.dev-dot.li | calculator.dot | A basic calculator |
| Stopwatch | https://stopwatch.dev-dot.li | stopwatch.dot | A stopwatch |
| Countdown Timer | https://countdown-timer.dev-dot.li | countdown-timer.dot | Counts down from a time you set |
| Alarm Clock | https://alarm-clock.dev-dot.li | alarm-clock.dot | Rings at the time you set |
| Chess Clock | https://chess-clock.dev-dot.li | chess-clock.dot | Two clocks for a two-player game; tap to pass the turn |
| Unit Converter | https://unit-converter.dev-dot.li | unit-converter.dot | Converts units, such as metres to feet |
Learn more¶
- Register a .dot domain: everything you can do with names
- Use platform services from the SDK: once your app is live
- App delivery: how publishing works
- How Bulletin authorization works: the storage pallet and quota model
| Created: July 16, 2026