# bulletin-deploy

> [!WARNING]
> The following is a prototype, reference implementation, and proof-of-concept. This open source code is provided for research, experimentation, and developer education only. This code has not been audited, is actively experimental, and may contain bugs, vulnerabilities, or incomplete features. Use at your own risk.

Deploy a static web app to the [Polkadot Bulletin Chain](https://github.com/paritytech/polkadot-bulletin-chain) and serve it under a human-readable `.dot` name — from one command. Names are resolved through DotNS, Polkadot's on-chain naming system.

It targets Polkadot **testnets** (default: `paseo-next-v2`). This is reference tooling for putting a static site on-chain, not a managed hosting service — you run the deploys yourself, and there is no server or account to sign up for.

[![npm](https://img.shields.io/npm/v/bulletin-deploy)](https://www.npmjs.com/package/bulletin-deploy)
[![License: GPL-3.0-or-later](https://img.shields.io/badge/license-GPL--3.0--or--later-blue.svg)](LICENSE)

## Features

- **On-chain static hosting** — Upload a built site (any framework, or plain HTML) to the Bulletin Chain and address it by a `.dot` name. No web server to run.
- **Mobile-wallet signing** — `login` once by scanning a QR with your Polkadot wallet; deploy with no mnemonic stored on disk.
- **Zero-signature testnet deploys** — a local worker registers the name and uploads the content, then transfers ownership to your signed-in account — no wallet taps in the default testnet flow.
- **Incremental uploads** — re-deploys only push the content chunks that changed, so updates are fast and cheap.
- **No native dependency required** — `--js-merkle` does content addressing in pure JavaScript; the IPFS Kubo binary is optional, not required.
- **Built-in environments** — target presets (RPC endpoints + contract addresses) selectable with `--env`, overridable per field.
- **Telemetry off by default** — opt-in only, and honors the `DO_NOT_TRACK` convention.

## Install

```sh
npm install -g bulletin-deploy
```

Requires **Node.js ≥ 22**. Content addressing uses the IPFS [Kubo](https://docs.ipfs.tech/install/) binary if it's on your `PATH`; otherwise pass `--js-merkle` to run it in pure JavaScript with no native dependency.

## Quick start

```sh
# Build your app, then deploy:
bulletin-deploy ./dist my-app.dot
```

Once it finishes, your site is served at `https://my-app.dot.li`.

New here? **[DEPLOYMENT.md](DEPLOYMENT.md)** walks you from prerequisites to a viewable app, step by step — including how to acquire a `.dot` name, what authorization a network requires (the raw "not authorized" chain error is a common first-run trap), and where a deployed site is viewable.

## Signing with a mobile wallet

Sign in once with your mobile Polkadot wallet — no mnemonic on disk:

```sh
bulletin-deploy login    # Scan the QR code with your Polkadot wallet app
bulletin-deploy whoami   # Show the currently signed-in address
bulletin-deploy logout   # Sign out and clear the session
```

After `login`, subsequent deploys hand the name to your signed-in account with **zero mobile signatures** (testnet): a local worker (the default dev account, or your `--mnemonic`) registers the name and uploads the content, then transfers ownership to your signed-in address as the final step. Pass `--no-transfer-to-signedin-user` to sign every DotNS transaction with your mobile session instead.

If a deploy's content lands but the final transfer fails, hand the name over separately:

```sh
bulletin-deploy transfer my-app.dot              # → the signed-in account
bulletin-deploy transfer my-app.dot --to 0x...   # → an explicit recipient
```

## Options

Run `bulletin-deploy --help` for the full option reference.

Key options:

| Option | Description |
|--------|-------------|
| `--env <id>` | Target environment (default: `paseo-next-v2`). Run `--list-environments` to see available IDs. |
| `--mnemonic "..."` | DotNS owner mnemonic (or set `MNEMONIC` env var). Alternative to session signing. |
| `--no-transfer-to-signedin-user` | When signed in, sign every DotNS tx with your mobile session instead of the default register-as-worker-then-hand-over flow. |
| `--to <0xH160>` | Recipient address for the `transfer` subcommand. Defaults to the signed-in account. |
| `--js-merkle` | Use pure-JS merkleization (no IPFS Kubo binary required). |
| `--config <path>` | Explicit path to `bulletin-deploy.config.ts` for product deploys. |
| `--tag "..."` | Label the deploy in telemetry. |
| `--version` | Print the installed version and exit. |

Subcommands: `login`, `logout`, `whoami` (session management, above) and `transfer <domain.dot>` (hand a name you registered to the signed-in account or `--to`).

## Naming

A DotNS label is 3-63 lowercase letters, digits, and hyphens, and it must satisfy the on-chain registration rules (PopRules) to be self-registrable. **The exact rules differ by DotNS generation** — trailing-digit handling and how the base length gets measured both vary — and bulletin-deploy detects which generation the connected chain is running and applies the right one automatically; a label that only satisfies one generation will break the moment it deploys to another.

If a name violates the connected generation's rules, the deploy fails with a message that offers a compliant alternative derived from your own input — no name gets silently substituted.

**The one rule that's safe everywhere:** keep the label's trailing digit run to exactly 0 or 2 digits, and keep the base (measured with that trailing run stripped) at **9 characters or more**. `myapp-preview07` — base `myapp-preview` at 13 chars — is `NoStatus` (self-registrable by anyone) on every generation. A shorter base isn't: `myapp-pr07` has an 8-char base (`myapp-pr`) on older DotNS generations, so it lands in the 6-8 band that demands proof of personhood — most callers don't have it, so the deploy fails — even though the same label measures 10 characters as written and clears the newest generation outright.

**PR-preview labels** are the case this trips up most often: a bare PR number gives exactly one trailing digit for PRs 1-9 (`myapp-pr7`), which older DotNS generations reject outright. Zero-padding alone isn't enough — `myapp-pr07` still fails on those same generations for the reason above. Pad the number **and** use a prefix long enough to clear the 9-character base:

```sh
bulletin-deploy ./dist "myapp-preview$(printf '%02d' "$PR_NUMBER")"
```

## Environments

bulletin-deploy ships with built-in environment presets (RPC endpoints, contract addresses). The default is `paseo-next-v2`, a Polkadot testnet. Use `--list-environments` to print the table; override individual fields with `--environment-file <path>` or `--contract KEY=0x...`. For what a starting account needs on a given network — funding, a `.dot` name, authorization — see **[DEPLOYMENT.md](DEPLOYMENT.md)**.

## Build from source

```sh
git clone https://github.com/paritytech/bulletin-deploy
cd bulletin-deploy
npm ci
npm run build
npm test          # offline unit tests
```

This produces the same `bulletin-deploy` CLI in `bin/`. See [`docs/testing.md`](docs/testing.md) for the live-testnet E2E suite.

## Releases

Releases use a two-stage flow: a release candidate is validated by the end-to-end test matrix against a live testnet, then the byte-identical source is published to npm under the stable version. Once Trusted Publishing is enabled, npm provenance will let you verify that a published release was built from this repository.

## Telemetry

Deploy telemetry (Sentry) is **off by default**. It activates only if you explicitly opt in with `BULLETIN_DEPLOY_TELEMETRY=1`, or when the CLI detects it is running in Parity's own CI. Set `BULLETIN_DEPLOY_TELEMETRY=0` — or the cross-tool `DO_NOT_TRACK=1` — to force it off in any context. The Sentry DSN baked into the package is a public project identifier, not a secret — it grants no access to collected data.

Separately from telemetry, the CLI checks the npm registry for the minimum supported version. Set `BULLETIN_DEPLOY_UPDATE_CHECK=0` to disable that check.

## Contributing

Issues and pull requests are welcome on the GitHub repository. Releases are cut by the maintainers. (Parity-internal design notes and investigation logs live in `docs-internal/`, which is not published to npm.)

## Security

> [!WARNING]
> The following is a prototype, reference implementation, and proof-of-concept. This open source code is provided for research, experimentation, and developer education only. This code has not been audited, is actively experimental, and may contain bugs, vulnerabilities, or incomplete features. Use at your own risk.

This repository contains reference and proof-of-concept code. Unless a specific release states otherwise, it has **not** received a full security audit. Use in production or production-like deployments should only follow an independent security review of the relevant code, configuration, generated output, and deployment environment.

Before deploying this for real use cases, you are responsible for:

- Reviewing the code yourself — we publish a reference, not a hardened production build.
- Checking that the dependencies are up to date and free of known vulnerabilities.
- Securing your own fork or deployment environment (keys, secrets, network configuration).
- Tracking the latest tagged release for security fixes; older releases are not backported (exceptions might apply).

Do **not** open a public issue for a suspected vulnerability. Follow Parity's responsible-disclosure process (`SECURITY.md`, inherited org-wide from [`paritytech/.github`](https://github.com/paritytech/.github/blob/main/SECURITY.md)) and email **security@parity.io**. For Parity's Bug Bounty programme, see https://parity.io/bug-bounty.

## License

[GPL-3.0-or-later](LICENSE). Versions up to and including 0.8.3 were published under Apache-2.0; the license changed to GPL-3.0-or-later ahead of the project being open-sourced.
