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
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Take a folder (or a single HTML file), turn it into a correct single-page-app
archive, and deploy it to the [Polkadot Bulletin Chain](https://github.com/paritytech/polkadot-bulletin-chain)
under a `.dot` name.
under a DotNS name.

> [!WARNING]
> Prototype / reference implementation. Not audited, actively experimental, and
Expand Down Expand Up @@ -35,7 +35,7 @@ decentralize ./dist --dot my-app -- --env summit --password hunter2

| Option | Meaning |
| --- | --- |
| `--dot <name>` | DotNS name, with or without `.dot`. Required. |
| `--dot <name>` | DotNS name. Required. The TLD comes from the target environment (`--env`) — e.g. paseo-next-v2 registers under `.paseo`. |
| `--path <dir>` | Explicit alternative to the bare positional source. |
| `--entry <file>` | Entry file to use as `index.html`, skipping auto-detection. |
| `--fallback` | Also write `404.html` + `_redirects` (off by default — see below). |
Expand All @@ -47,6 +47,19 @@ verbatim (`--env`, `--password`, `--publish`, `--mnemonic`, `--js-merkle`, …).
Use `--` to end this tool's own parsing when a forwarded flag would otherwise be
ambiguous.

**The TLD comes from the target environment, not from `--dot`.** `--dot` only
carries the label — `my-app`, not `my-app.paseo`. This tool forwards that
label to `bulletin-deploy`, which applies whichever suffix the environment you
deploy to (`--env`, itself forwarded — see above) actually uses:
`paseo-next-v2` (the default) registers under `.paseo`; `preview` still uses
`.dot`; most other environments carry no fixed suffix at all. A trailing
`.dot` typed on `--dot` is stripped for backward compatibility (every example
before bulletin-deploy 0.15.0 spelled it out), but this tool does not append
one — guessing the right suffix per environment would mean copying
bulletin-deploy's environment table here, and that copy would rot the moment
a new network is added upstream. Pass the bare label and let bulletin-deploy
pick the suffix.

## What it actually does

1. **Copies** your source into a temp staging directory. Your build output is
Expand All @@ -61,7 +74,8 @@ ambiguous.
root. Ambiguity is an error naming the candidates, not a guess.
4. **Writes nothing else by default.** `404.html` + `_redirects` are only
written with `--fallback` (see below for why that's the default now).
5. **Runs `bulletin-deploy`** against the staged directory with your `.dot` name.
5. **Runs `bulletin-deploy`** against the staged directory with your DotNS
label — bare, so bulletin-deploy applies the target environment's own TLD.

## Why `index.html` at the root is the whole point

Expand Down Expand Up @@ -121,14 +135,17 @@ DotNS (PopRules) accepts a label with **exactly zero or two trailing digits**;
anything else reverts on-chain. bulletin-deploy responds by *rewriting* such
labels — and on 0.13.x it did so on the registration path, silently retargeting
the deploy at a different name ([its issue #1189](https://www.npmjs.com/package/bulletin-deploy)).
Observed live: `--dot my-app3` became `my-app.dot`, an already-owned live name,
and the deploy went on to offer to overwrite its content.
Observed live: `--dot my-app3` became `my-app.dot` (`.dot` was the only TLD
that existed at the time), an already-owned live name, and the deploy went on
to offer to overwrite its content. The hazard — silently landing on a
different, already-owned name — is the same regardless of which TLD the
target environment uses today.

This tool refuses such labels up front and tells you what they would have become:

```
✖ --dot "my-app3" has 1 trailing digit; DotNS (PopRules) accepts exactly 0 or 2.
bulletin-deploy would rewrite it to "my-app.dot" instead of failing …
bulletin-deploy would rewrite it to "my-app" instead of failing …
```

Use a label ending in a letter, or in exactly two digits (`my-app01`).
Expand Down
139 changes: 112 additions & 27 deletions e2e/BOOTSTRAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,29 @@ here is audited or hardened for production use.
```sh
e2e/bootstrap.sh # check-only, read-only, never touches the chain
e2e/bootstrap.sh --fix # additionally runs `ipfs init` if the repo is missing
e2e/bootstrap.sh --register # the one real deploy that (re)claims decentralize-ci.dot
e2e/bootstrap.sh --register # the one real deploy that (re)claims decentralize-ci.paseo
```

## 1. `decentralize-ci.dot`

**What it is:** a fixed DotNS label on Paseo Next v2, registered 2026-08-07.
## 1. `decentralize-ci.paseo`

> **Update, 2026-08-18:** the label's TLD changed. bulletin-deploy 0.15.0 made
> the TLD per-environment, and paseo-next-v2 (this suite's `ENV_ID`) registers
> under `.paseo`, not `.dot` — the name below was re-registered as
> `decentralize-ci.paseo` (see item 4's update for the full re-genesis story).
> Everywhere below that still says `.dot` in a command example is preserved
> as-written for history; the live, current name is `decentralize-ci.paseo`,
> and `decentralize` itself now forwards the bare label `decentralize-ci` and
> lets bulletin-deploy apply that suffix (see the main README's "Naming"
> section) — do not hand it `decentralize-ci.dot` directly, that now fails
> with `Domain "decentralize-ci.dot" ends in ".dot", but this environment
> uses ".paseo" names.`

**What it is:** a fixed DotNS label on Paseo Next v2, originally registered
2026-08-07 as `decentralize-ci.dot`, then re-registered 2026-08-18 as
`decentralize-ci.paseo` after the chain re-genesis described in item 4's
update (a re-genesis resets on-chain state, so the original registration did
not carry forward — this is the "How to recreate it if lost" path below,
exercised for real).

**Why it's needed:** the suite re-deploys over the same name every run rather
than minting a fresh one per run. That keeps on-chain state bounded (one
Expand All @@ -35,8 +52,9 @@ built-in worker `5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV` — the
pool-fallback signer bulletin-deploy uses when no session and no
`--mnemonic` are supplied.

**Why it classifies as free (NoStatus):** from `classifyLabelStatus` in the
pinned bulletin-deploy:
**Why it classifies as NoStatus (open to any account — see item 4's update
for why "NoStatus" no longer also means "free"):** from `classifyLabelStatus`
in the pinned bulletin-deploy:

| Label shape | Required status |
| --- | --- |
Expand All @@ -55,16 +73,18 @@ failure.

**How to recreate it if lost:** simply run a deploy against it. Any deploy —
the e2e suite's own run, `e2e/bootstrap.sh --register`, or a manual
`bulletin-deploy <dir> decentralize-ci.dot` — re-registers/overwrites it. There
is no separate "provisioning" step; deploying *is* the recovery mechanism.
`bulletin-deploy <dir> decentralize-ci --env paseo-next-v2` (bare label — see
the 2026-08-18 update above) — re-registers/overwrites it. There is no
separate "provisioning" step; deploying *is* the recovery mechanism.

**The sharp edge (accepted risk, not hidden):** ownership sits with
bulletin-deploy's **shared default dev worker**, not an account this project
controls. That worker is derived deterministically for *anyone* who runs
bulletin-deploy with no session and no `--mnemonic` against Paseo Next v2 —
it is not scoped to this repo or this CI. Concretely: anyone, anywhere,
running plain `bulletin-deploy <dir> decentralize-ci.dot` with no session
would be the *same* signer/owner and could silently overwrite our content.
running plain `bulletin-deploy <dir> decentralize-ci --env paseo-next-v2` with
no session would be the *same* signer/owner and could silently overwrite our
content.
There is no ownership check we can add on our side to prevent this — the
worker's identity is bulletin-deploy's, not ours. This is a real, accepted
risk given the suite's scope (one disposable testnet fixture, not a
Expand All @@ -86,15 +106,15 @@ worker and then **transfer the name to the signed-in account** with zero
mobile signatures (`--no-transfer-to-signedin-user` is the opt-out — see
`bulletin-deploy --help`). A developer who is signed in on their own machine
and runs `e2e/bootstrap.sh --register` — or any bare `bulletin-deploy <dir>
decentralize-ci.dot` — without realizing it would silently move the name off
the pool-fallback worker and onto their personal account. That's worse than
the stranger case above: it's not recoverable by re-running `--register`
from CI, because CI's pool worker no longer owns the name to overwrite.
`e2e/bootstrap.sh --register` checks `bulletin-deploy whoami` first and
refuses to run while a session is signed in, naming `bulletin-deploy logout`
as the remedy — but this check only covers this script's own `--register`
path, not a developer running bulletin-deploy directly by hand. Log out
before touching `decentralize-ci.dot` directly.
decentralize-ci --env paseo-next-v2` — without realizing it would silently
move the name off the pool-fallback worker and onto their personal account.
That's worse than the stranger case above: it's not recoverable by re-running
`--register` from CI, because CI's pool worker no longer owns the name to
overwrite. `e2e/bootstrap.sh --register` checks `bulletin-deploy whoami` first
and refuses to run while a session is signed in, naming `bulletin-deploy
logout` as the remedy — but this check only covers this script's own
`--register` path, not a developer running bulletin-deploy directly by hand.
Log out before touching `decentralize-ci.paseo` directly.

## 2. Kubo (`ipfs` binary + initialized repo)

Expand Down Expand Up @@ -193,8 +213,47 @@ reporting section.

## 4. The worker's `ProofOfPersonhoodFull` status

**What it is:** the pool-fallback signer (`5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV`)
currently carries `ProofOfPersonhoodFull`.
> **Update, 2026-08-18 — this predicted failure mode happened.** Paseo Next
> v2 was re-genesised, and the pool-fallback worker
> (`5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV`) came back with **no PoP
> status at all**. `bulletin-deploy`'s preflight now reports `Your PoP:
> NoStatus` for it — exactly the "single most likely way the design silently
> stops working" scenario called out below, before it had actually happened.
> The section below is left as originally written (it is still an accurate
> description of *why* the suite used to cost nothing); read it as history,
> then read this update for the current state:
>
> - **Deploys are no longer free.** A live registration against
> `decentralize-ci.paseo` reported `Oracle price: 10 PAS / Paying: 11 PAS`
> — `registerDepositWei` now takes the `NoStatus` branch (`startingPriceWei`)
> exactly as predicted. Separately, bulletin-deploy's preflight also prints
> a balance-floor figure (observed: **211.1 PAS**) that the signer must hold
> to proceed — that figure is a *minimum balance requirement*, not the
> price of this deploy; don't confuse the two when reading its output.
> - **The worker was funded** to cover this: topped up via the public faucet
> (<https://faucet.polkadot.io/?parachain=1500>) to **~5005 PAS**, at
> `5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV` — the same address as
> above. **That is the address to top up** if this suite ever starts
> failing on a balance error again.
> - **The subtle trap: bulletin-deploy's own auto-top-up cannot rescue this
> worker.** bulletin-deploy has a dev-convenience path that auto-tops-up a
> low-balance signer from "Alice". That "Alice" is **the root account of
> the dev mnemonic** (`//` with no derivation path) — and the pool-fallback
> worker *is* that same root account, not `//Alice` (the well-known
> `5GrwvaEF…` test account derived from it). The auto-top-up code compares
> the source and recipient addresses and **skips the transfer whenever they
> are equal** — so when the worker itself is the signer, "Alice" funding
> the worker is a no-op by construction: the source and destination are the
> same account. Funding the derived `//Alice` (`5GrwvaEF…`) does **nothing**
> for this worker — do not waste a faucet request on it. Fund
> `5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV` directly.
> - **Current registration:** `decentralize-ci.paseo` is registered and owned
> by `0x35Cdb23fF7fc86E8DCcd577CA309bFEA9c978D20` (the same worker, EVM-mapped
> — matches item 1's "Current owner").
>
> **What it was (history, before the re-genesis):** the pool-fallback signer
> (`5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV`) carried
> `ProofOfPersonhoodFull`.

**Why this is the whole reason the suite costs nothing:** the price rule
lives in the pinned bulletin-deploy and keys on the **signer's** status, not
Expand Down Expand Up @@ -230,7 +289,17 @@ If nightly runs start failing on a price/balance error where they previously
didn't, check this first, before assuming a `decentralize` or
`bulletin-deploy` regression.

## 5. Pinned `bulletin-deploy@0.14.2`
## 5. Pinned `bulletin-deploy@0.15.0`

> **Update, 2026-08-18:** bumped from `0.14.2` to `0.15.0`
> (`deps/bulletin-deploy-0.15.0`). The section below still says `0.14.2` in
> places where it is describing that specific version's behaviour verbatim
> ("verified by hand against 0.14.2" etc.) — left as-written, since it is
> accurate history, not updated to imply it was re-verified against 0.15.0
> line-by-line. The one thing this bump changed that matters everywhere in
> this document: `assets/environments.json` gained a per-environment `tld`
> field, and `paseo-next-v2` (this suite's `ENV_ID`) now uses `.paseo` instead
> of `.dot` — see item 1's update and the main README's "Naming" section.

**What it is:** `decentralize`'s own pinned dependency (`package.json` →
`dependencies.bulletin-deploy`), and the two public exports the e2e suite
Expand Down Expand Up @@ -270,8 +339,11 @@ a mismatch by the check above):
import paths with compatible signatures — the check above catches this
directly.
3. The price rule (`userStatus === NoStatus ? startingPriceWei : 0n`) is
still keyed the same way — a change here could reintroduce a cost where
today there is none.
still keyed the same way. (This branch is no longer hypothetical: as of
the 2026-08-18 update in item 4, the worker IS `NoStatus` and every run
now pays `startingPriceWei` — observed as `Oracle price: 10 PAS / Paying:
11 PAS`. Re-verify this rule's shape on the next bump precisely because a
change here changes what the suite pays, not whether it pays at all.)
4. The CAR-vs-manifest relationship in the design doc ("The on-chain
contenthash is the CAR, not the UnixFS directory") still holds — this is
what the whole assertion chain in the design depends on.
Expand All @@ -292,12 +364,19 @@ confirming whether the free-deploy premise still holds at all — a secret
papering over a broken premise just hides the regression instead of
surfacing it.

> **Update, 2026-08-18:** the trigger above fired — the worker's PoP status
> lapsed (item 4) and the suite now pays `~11 PAS` per run. The rule in this
> section still holds regardless: the fix was to **fund the worker directly**
> (item 4's faucet top-up to ~5005 PAS), not to add a `MNEMONIC` secret. "No
> secrets" was never a claim that deploys are free forever — it's a claim
> that this suite doesn't authenticate as anyone. It still doesn't.

## Known unknowns

Stated honestly rather than assumed away:

- **Whether DotNS registrations expire.** Not established. If they do, the
window before `decentralize-ci.dot` needs a fresh registration (as opposed
window before `decentralize-ci.paseo` needs a fresh registration (as opposed
to an overwrite of an existing one) is unknown. Both paths are handled by
bulletin-deploy either way (see item 1's "how to recreate"), so this
doesn't block the suite — it's flagged so nobody mistakes a
Expand All @@ -318,5 +397,11 @@ Stated honestly rather than assumed away:
bulletin-deploy ever exposes a genuinely lightweight read-only query for
this, revisit.
- **Whether the worker's `ProofOfPersonhoodFull` status can lapse, and on
what schedule.** Not established — see item 4. Worth monitoring, not
currently monitorable from outside a full deploy attempt.
what schedule.** Partially resolved, 2026-08-18: it can, and did — see item
4's update. What's still unknown is the *schedule*: this instance was
triggered by a chain re-genesis, not a natural expiry, so whether
`ProofOfPersonhoodFull` also lapses on its own over time (independent of a
re-genesis event) remains unestablished. Still not monitorable from outside
a full deploy attempt (or a preflight-only run — see item 4's `Your PoP:
NoStatus` observation, which came from bulletin-deploy's own preflight
output, not a purpose-built check on our side).
Loading