Skip to content

chore(release): v0.11.0 - #24

Merged
jdogresorg merged 13 commits into
masterfrom
release/v0.11.0
Aug 26, 2026
Merged

chore(release): v0.11.0#24
jdogresorg merged 13 commits into
masterfrom
release/v0.11.0

Conversation

@jdogresorg

Copy link
Copy Markdown
Contributor

Cuts the carrier for the v0.11.0 train.

src/release-manifest.json pins the twelve component tags this train produced, the carrier bumps to 0.11.0, and the changelog is dated.

Every commit in the manifest was read back from its tag on origin rather than from local state, and each was checked three ways before being written:

  • the repository's v0.11.0 tag dereferences to that commit
  • GitHub reports the tag verified, signed by the platform release key and taggered releases@xchain.io
  • the commit is that repository's master tip, and is the merge commit whose CI is green

The pinned set is the full twelve in modulesUrls, so a pinned install of this train clones nothing at a default branch.

Gate: npm run ci:full green on test02 at 3369 pass / 0 fail against this exact commit.

Bitcoin, Litecoin, and Dogecoin are where the platform runs today, not the
definition of it; the README says so where it names them.
Module clones use the public HTTPS GitHub URLs, so a machine without a
GitHub SSH key can install; SSH and local-source workflows keep the
XCHAIN_NODE_MODULES_URLS_OVERRIDE escape hatch.

Downloading a coin node creates the crypto-nodes directory before
opening the write stream. The default directory ships in the repo, so
this only ever failed for installs pointing CRYPTO_NODES_DIR at a fresh
volume.

Bootstrap auto-restore routes its download directory through
ensureDirWritable, recovering a destination a service container already
created root-owned, the same failure the create path already handles.

The explorer install health wait grows from ten seconds to about two
minutes, covering a cold container's warm-up instead of reporting a
hard failure a rerun immediately contradicts.
bitcoincore.org is several mirrors behind one name and they are not
equivalent: at least one serves a leaf-only certificate chain, which
Node rejects where curl and browsers recover by fetching the missing
intermediate. Whoever DNS sent there could not install at all, and
retrying did not help because the resolver kept returning the same
address. A failed download now enumerates the site's addresses and
tries each one, pinning only which mirror is dialled: the URL, SNI and
certificate check still run against the hostname, and the pinned
SHA-256 is still verified before the tarball is used. Only transport
failures widen this way, since every mirror answers a 404 alike, and
the ordinary path is unchanged. Failures name the URL, the mirror and
the cause, plus a way forward when all of them fail.

Separately, a bootstrap restore that fails leaves the service syncing
from block 0, which is hours to days of work, and its only trace was
one warning in the middle of a long install log. Install and update now
end with a restore summary per service. Because a service that starts
syncing no longer reads as fresh, the failed attempt was also the only
attempt; XCHAIN_NODE_FORCE_BOOTSTRAP=1 takes it again, kept opt-in
because the restore wipes the data directory.
The launcher resolved src/index.js relative to the caller's working
directory, so invoking the installed symlink from anywhere other than the
checkout failed to find the entry point. Scheduled jobs, which do not
change directory first, were the ones this broke.

Resolve the script's own location through the symlink and change into it
before starting node.
Four defects an external operator hit reinstalling clean on ARM64, each
verified against the code before it was changed.

The mirror failover could never succeed. Since Node 20 autoSelectFamily
is on by default, so net.connect calls a custom lookup with all:true and
expects an array of records; the pinned lookup answered with the single
address form, and every retry died reading address off undefined before
a socket was opened. Three tests asserted the retry existed and none of
them ever called it the way Node does.

The remediation those failures print asked for a file and then destroyed
it. It tells an operator whose mirrors all serve a broken certificate
chain to fetch the tarball by hand, place it, and re-run, but the re-run
went straight into the download, truncated the placed file, and deleted
it in the catch. A tarball already at the path is now accepted against
the pinned hash and the download skipped. The hash is checked at the
point of acceptance rather than only at the gate before decompression,
so a corrupt leftover from an interrupted run is still discarded and
re-downloaded instead of failing the install outright.

The bootstrap restore summary was printed after the install loop with
nothing guarding it, so a run that threw partway printed nothing. That
is the run whose partial restores an operator most needs accounted for.
It is now in a finally, and the failure still propagates.

A published bootstrap is not a free starting point: a service walks
forward from the restored tip, and one that has aged past the chain it
lands on can be left unable to continue, which for the utxo-tracker
means a halt and a full rebuild. The age of the archive that was
actually resolved is now reported during the download, with a warning
past ten days. Ten rather than seven because testnet trackers publish
weekly, so a healthy archive reaches six days old routinely and a
warning that fires on healthy state is one nobody reads.
The indexer tracks reorgs by a decoder event id, and the decoder never
deletes those rows, so wiping the decoder alone restarts the ids
underneath a cursor that now points past them. The indexer then aborts
with a reorg-cursor error and stops committing blocks. An operator
recovering a stuck decoder hit exactly this, and reset let them do it.

`reset xchain-decoder` now checks for an installed indexer and refuses
before anything is stopped or wiped, naming the joint form rather than
just the problem. `--with-indexer` resets the pair together, which also
clears the hub price fence the wiped indexer would otherwise trip.

The coupling is one-directional, so an indexer-only reset stays allowed:
it re-derives from an intact decoder, which is an ordinary reindex. A
decoder-only reset also stays allowed where no indexer is installed to
strand. Six tests, falsified against the unguarded path.
The refusal named a scoped migrate command to run inside the container
being replaced. That container runs the build being upgraded away from,
and a build without per-file targeting does not reject the flag: it
ignores it and applies every pending manual migration, which on a live
database can mean a data backfill and a dedup-then-unique nobody asked
for.

The refusal now reads the target container's own migrate entry point and
prints the scoped command only when that build is confirmed to honour
it. When the capability is absent, or the container cannot be read at
all, it prints no runnable command and instead names every pending
manual migration an unscoped run would apply, marking the one that is
actually needed.

The probe runs only on the refusal path, so a healthy deploy is
unaffected and a probe that fails still refuses.
…ot read

The external-DB halt-marker probe passed a clustered short-option string the
native command parser did not recognise as batch mode, so every SELECT returned
empty and an unreadable probe certified a halted decoder as clean. The parser
now understands clustered flags and the gate treats an unreadable answer as a
refusal rather than as absence of a marker.
The fee-destination comment in the vendored coin bundles said the override
was env-overridable after it had been restricted to regtest. Those three files
are vendored byte-identically across the fleet behind conformance guards, so the
correction is propagated rather than applied in one place.
Pins the twelve component tags this train cut, bumps the carrier to 0.11.0 and
dates the changelog.

Every commit in the manifest was read back from its tag on origin rather than
from local state, and each was checked three ways before being written: the
v0.11.0 tag dereferences to that commit, GitHub reports the tag verified against
the release identity, and the commit is that repository's master tip. All twelve
are master merge commits whose CI is green.
@jdogresorg

Copy link
Copy Markdown
Contributor Author

recheck

@jdogresorg jdogresorg closed this Aug 26, 2026
@jdogresorg jdogresorg reopened this Aug 26, 2026
@jdogresorg
jdogresorg merged commit 2e568ee into master Aug 26, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant