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.
Prerequisites
Section titled “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 # dotnsnpm i -g @polkadot-community-foundation/polkadot-app-deploy@latest # pad, pad-bootstrapInstall 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:
npm i -g --ignore-scripts @polkadot-community-foundation/dotns-cli@latestIn 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
Section titled “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
Section titled “Authorize from the CLI”dotns can also grant the authorization directly, signed by the shared devnet
authorizer:
dotns bulletin authorize <your-ss58-address> \ --transactions 1000 --bytes 104857600 --env devnetThis 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
Section titled “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:
pad-bootstrap --env devnet --authorizer "//Eve"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
Section titled “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
Section titled “2. Build the frontend”Produce a static build directory. With a Vite template:
npm installnpm run build # emits dist/The output (dist/) is what you publish.
3. Register a .dot domain
Section titled “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:
dotns lookup name <name> --env devnetdotns register domain -n <name> --env devnetPass 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
Section titled “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:
pad ./dist <name>.dot --env devnet --mnemonic "$MNEMONIC"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:
dotns content view <name> --env devnetThe 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
Section titled “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: bafk2bzacecqdzkvdzflim3gspebc2iaqfjxtmqbuo6lpaqmvmlfhvkqxl2klwWriting 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
Section titled “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
Section titled “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